You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JGitUtils.java 25KB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. package com.gitblit.utils;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.RandomAccessFile;
  7. import java.text.DateFormat;
  8. import java.text.ParseException;
  9. import java.text.SimpleDateFormat;
  10. import java.util.ArrayList;
  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. import org.eclipse.jgit.diff.DiffEntry;
  18. import org.eclipse.jgit.diff.DiffFormatter;
  19. import org.eclipse.jgit.diff.RawTextComparator;
  20. import org.eclipse.jgit.diff.DiffEntry.ChangeType;
  21. import org.eclipse.jgit.errors.ConfigInvalidException;
  22. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  23. import org.eclipse.jgit.errors.MissingObjectException;
  24. import org.eclipse.jgit.errors.StopWalkException;
  25. import org.eclipse.jgit.lib.AnyObjectId;
  26. import org.eclipse.jgit.lib.Constants;
  27. import org.eclipse.jgit.lib.FileMode;
  28. import org.eclipse.jgit.lib.ObjectId;
  29. import org.eclipse.jgit.lib.ObjectLoader;
  30. import org.eclipse.jgit.lib.PersonIdent;
  31. import org.eclipse.jgit.lib.Ref;
  32. import org.eclipse.jgit.lib.Repository;
  33. import org.eclipse.jgit.lib.StoredConfig;
  34. import org.eclipse.jgit.revwalk.RevBlob;
  35. import org.eclipse.jgit.revwalk.RevCommit;
  36. import org.eclipse.jgit.revwalk.RevObject;
  37. import org.eclipse.jgit.revwalk.RevTree;
  38. import org.eclipse.jgit.revwalk.RevWalk;
  39. import org.eclipse.jgit.revwalk.filter.RevFilter;
  40. import org.eclipse.jgit.treewalk.TreeWalk;
  41. import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
  42. import org.eclipse.jgit.treewalk.filter.PathFilter;
  43. import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
  44. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  45. import org.eclipse.jgit.util.io.DisabledOutputStream;
  46. import org.slf4j.Logger;
  47. import org.slf4j.LoggerFactory;
  48. import com.gitblit.wicket.models.Metric;
  49. import com.gitblit.wicket.models.PathModel;
  50. import com.gitblit.wicket.models.PathModel.PathChangeModel;
  51. import com.gitblit.wicket.models.RefModel;
  52. import com.gitblit.wicket.models.TicketModel;
  53. import com.gitblit.wicket.models.TicketModel.Comment;
  54. public class JGitUtils {
  55. /** Prefix for notes refs */
  56. public static final String R_NOTES = "refs/notes/";
  57. /** Standard notes ref */
  58. public static final String R_NOTES_COMMITS = R_NOTES + "commits";
  59. private final static Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
  60. public static List<String> getRepositoryList(File repositoriesFolder, boolean exportAll, boolean readNested) {
  61. List<String> list = new ArrayList<String>();
  62. list.addAll(getNestedRepositories(repositoriesFolder, repositoriesFolder, exportAll, readNested));
  63. Collections.sort(list);
  64. return list;
  65. }
  66. public static List<String> getNestedRepositories(File repositoriesFolder, File folder, boolean exportAll, boolean readNested) {
  67. String basefile = repositoriesFolder.getAbsolutePath();
  68. List<String> list = new ArrayList<String>();
  69. for (File file : folder.listFiles()) {
  70. if (file.isDirectory() && !file.getName().equalsIgnoreCase(Constants.DOT_GIT)) {
  71. // if this is a git repository add it to the list
  72. File gitFolder = new File(file, Constants.DOT_GIT);
  73. boolean isGitRepository = gitFolder.exists() && gitFolder.isDirectory();
  74. boolean exportRepository = isGitRepository && (exportAll || new File(gitFolder, "git-daemon-export-ok").exists());
  75. if (exportRepository) {
  76. // determine repository name relative to repositories folder
  77. String filename = file.getAbsolutePath();
  78. String repo = filename.substring(basefile.length()).replace('\\', '/');
  79. if (repo.charAt(0) == '/') {
  80. repo = repo.substring(1);
  81. }
  82. list.add(repo);
  83. }
  84. // look for nested repositories
  85. if (readNested) {
  86. list.addAll(getNestedRepositories(repositoriesFolder, file, exportAll, readNested));
  87. }
  88. }
  89. }
  90. return list;
  91. }
  92. public static Date getLastChange(Repository r) {
  93. RevCommit commit = getCommit(r, Constants.HEAD);
  94. return getCommitDate(commit);
  95. }
  96. public static RevCommit getCommit(Repository r, String objectId) {
  97. RevCommit commit = null;
  98. try {
  99. if (objectId == null || objectId.trim().length() == 0) {
  100. objectId = Constants.HEAD;
  101. }
  102. ObjectId object = r.resolve(objectId);
  103. RevWalk walk = new RevWalk(r);
  104. RevCommit rev = walk.parseCommit(object);
  105. commit = rev;
  106. walk.dispose();
  107. } catch (Throwable t) {
  108. LOGGER.error("Failed to determine last change", t);
  109. }
  110. return commit;
  111. }
  112. public static Map<ObjectId, List<String>> getAllRefs(Repository r) {
  113. Map<ObjectId, List<String>> refs = new HashMap<ObjectId, List<String>>();
  114. Map<AnyObjectId, Set<Ref>> allRefs = r.getAllRefsByPeeledObjectId();
  115. for (AnyObjectId id : allRefs.keySet()) {
  116. List<String> list = new ArrayList<String>();
  117. for (Ref setRef : allRefs.get(id)) {
  118. String name = setRef.getName();
  119. list.add(name);
  120. }
  121. refs.put(id.toObjectId(), list);
  122. }
  123. return refs;
  124. }
  125. public static Map<ObjectId, List<String>> getRefs(Repository r, String baseRef) {
  126. Map<ObjectId, List<String>> refs = new HashMap<ObjectId, List<String>>();
  127. Map<AnyObjectId, Set<Ref>> allRefs = r.getAllRefsByPeeledObjectId();
  128. for (AnyObjectId id : allRefs.keySet()) {
  129. List<String> list = new ArrayList<String>();
  130. for (Ref setRef : allRefs.get(id)) {
  131. String name = setRef.getName();
  132. if (name.startsWith(baseRef)) {
  133. list.add(name);
  134. }
  135. }
  136. refs.put(id.toObjectId(), list);
  137. }
  138. return refs;
  139. }
  140. /**
  141. * Lookup an entry stored in a tree, failing if not present.
  142. *
  143. * @param tree
  144. * the tree to search.
  145. * @param path
  146. * the path to find the entry of.
  147. * @return the parsed object entry at this path
  148. * @throws Exception
  149. */
  150. public static RevObject getRevObject(Repository r, final RevTree tree, final String path) {
  151. RevObject ro = null;
  152. RevWalk rw = new RevWalk(r);
  153. TreeWalk tw = new TreeWalk(r);
  154. tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path)));
  155. try {
  156. tw.reset(tree);
  157. while (tw.next()) {
  158. if (tw.isSubtree() && !path.equals(tw.getPathString())) {
  159. tw.enterSubtree();
  160. continue;
  161. }
  162. ObjectId entid = tw.getObjectId(0);
  163. FileMode entmode = tw.getFileMode(0);
  164. ro = rw.lookupAny(entid, entmode.getObjectType());
  165. rw.parseBody(ro);
  166. }
  167. } catch (Throwable t) {
  168. LOGGER.error("Can't find " + path + " in tree " + tree.name(), t);
  169. } finally {
  170. if (rw != null) {
  171. rw.dispose();
  172. }
  173. }
  174. return ro;
  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. public static String getRawContentAsString(Repository r, RevBlob blob) {
  193. return new String(getRawContent(r, blob));
  194. }
  195. public static String getRawContentAsString(Repository r, RevCommit commit, String blobPath) {
  196. RevObject obj = getRevObject(r, commit.getTree(), blobPath);
  197. return new String(getRawContent(r, (RevBlob) obj));
  198. }
  199. public static List<PathModel> getFilesInPath(Repository r, String basePath, String objectId) {
  200. RevCommit commit = getCommit(r, objectId);
  201. return getFilesInPath(r, basePath, commit);
  202. }
  203. public static List<PathModel> getFilesInPath(Repository r, String basePath, RevCommit commit) {
  204. List<PathModel> list = new ArrayList<PathModel>();
  205. final TreeWalk walk = new TreeWalk(r);
  206. try {
  207. walk.addTree(commit.getTree());
  208. if (basePath != null && basePath.length() > 0) {
  209. PathFilter f = PathFilter.create(basePath);
  210. walk.setFilter(f);
  211. walk.setRecursive(false);
  212. boolean foundFolder = false;
  213. while (walk.next()) {
  214. if (!foundFolder && walk.isSubtree()) {
  215. walk.enterSubtree();
  216. }
  217. if (walk.getPathString().equals(basePath)) {
  218. foundFolder = true;
  219. continue;
  220. }
  221. if (foundFolder) {
  222. list.add(getPathModel(walk, basePath, commit));
  223. }
  224. }
  225. } else {
  226. walk.setRecursive(false);
  227. while (walk.next()) {
  228. list.add(getPathModel(walk, null, commit));
  229. }
  230. }
  231. } catch (IOException e) {
  232. LOGGER.error("Failed to get files for commit " + commit.getName(), e);
  233. } finally {
  234. walk.release();
  235. }
  236. Collections.sort(list);
  237. return list;
  238. }
  239. public static List<PathChangeModel> getFilesInCommit(Repository r, String commitId) {
  240. RevCommit commit = getCommit(r, commitId);
  241. return getFilesInCommit(r, commit);
  242. }
  243. public static List<PathChangeModel> getFilesInCommit(Repository r, RevCommit commit) {
  244. List<PathChangeModel> list = new ArrayList<PathChangeModel>();
  245. try {
  246. final RevWalk rw = new RevWalk(r);
  247. RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
  248. RevTree parentTree = parent.getTree();
  249. RevTree commitTree = commit.getTree();
  250. final TreeWalk walk = new TreeWalk(r);
  251. walk.reset();
  252. walk.setRecursive(true);
  253. walk.addTree(parentTree);
  254. walk.addTree(commitTree);
  255. walk.setFilter(TreeFilter.ANY_DIFF);
  256. RawTextComparator cmp = RawTextComparator.DEFAULT;
  257. DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);
  258. df.setRepository(r);
  259. df.setDiffComparator(cmp);
  260. df.setDetectRenames(true);
  261. List<DiffEntry> diffs = df.scan(parentTree, commitTree);
  262. for (DiffEntry diff : diffs) {
  263. if (diff.getChangeType().equals(ChangeType.DELETE)) {
  264. list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff.getNewMode().getBits(), commit.getId().getName(), diff.getChangeType()));
  265. } else {
  266. list.add(new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff.getNewMode().getBits(), commit.getId().getName(), diff.getChangeType()));
  267. }
  268. }
  269. } catch (Throwable t) {
  270. LOGGER.error("failed to determine files in commit!", t);
  271. }
  272. return list;
  273. }
  274. public static String getCommitDiff(Repository r, RevCommit commit, boolean outputHtml) {
  275. return getCommitDiff(r, null, commit, null, outputHtml);
  276. }
  277. public static String getCommitDiff(Repository r, RevCommit commit, String path, boolean outputHtml) {
  278. return getCommitDiff(r, null, commit, path, outputHtml);
  279. }
  280. public static String getCommitDiff(Repository r, RevCommit baseCommit, RevCommit commit, boolean outputHtml) {
  281. return getCommitDiff(r, baseCommit, commit, null, outputHtml);
  282. }
  283. public static String getCommitDiff(Repository r, RevCommit baseCommit, RevCommit commit, String path, boolean outputHtml) {
  284. try {
  285. RevTree baseTree;
  286. if (baseCommit == null) {
  287. final RevWalk rw = new RevWalk(r);
  288. RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
  289. rw.dispose();
  290. baseTree = parent.getTree();
  291. } else {
  292. baseTree = baseCommit.getTree();
  293. }
  294. RevTree commitTree = commit.getTree();
  295. final TreeWalk walk = new TreeWalk(r);
  296. walk.reset();
  297. walk.setRecursive(true);
  298. walk.addTree(baseTree);
  299. walk.addTree(commitTree);
  300. walk.setFilter(TreeFilter.ANY_DIFF);
  301. final ByteArrayOutputStream os = new ByteArrayOutputStream();
  302. RawTextComparator cmp = RawTextComparator.DEFAULT;
  303. DiffFormatter df;
  304. if (outputHtml) {
  305. df = new HtmlDiffFormatter(os);
  306. } else {
  307. df = new DiffFormatter(os);
  308. }
  309. df.setRepository(r);
  310. df.setDiffComparator(cmp);
  311. df.setDetectRenames(true);
  312. List<DiffEntry> diffs = df.scan(baseTree, commitTree);
  313. if (path != null && path.length() > 0) {
  314. for (DiffEntry diff : diffs) {
  315. if (diff.getNewPath().equalsIgnoreCase(path)) {
  316. df.format(diff);
  317. break;
  318. }
  319. }
  320. } else {
  321. df.format(diffs);
  322. }
  323. String diff;
  324. if (outputHtml) {
  325. // workaround for complex private methods in DiffFormatter
  326. diff = ((HtmlDiffFormatter) df).getHtml();
  327. } else {
  328. diff = os.toString();
  329. }
  330. df.flush();
  331. return diff;
  332. } catch (Throwable t) {
  333. LOGGER.error("failed to generate commit diff!", t);
  334. }
  335. return null;
  336. }
  337. public static String getCommitPatch(Repository r, RevCommit commit) {
  338. return getCommitPatch(r, commit);
  339. }
  340. public static String getCommitPatch(Repository r, RevCommit commit, String path) {
  341. return getCommitPatch(r, null, commit, path);
  342. }
  343. public static String getCommitPatch(Repository r, RevCommit baseCommit, RevCommit commit, String path) {
  344. try {
  345. RevTree baseTree;
  346. if (baseCommit == null) {
  347. final RevWalk rw = new RevWalk(r);
  348. RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
  349. baseTree = parent.getTree();
  350. } else {
  351. baseTree = baseCommit.getTree();
  352. }
  353. RevTree commitTree = commit.getTree();
  354. final TreeWalk walk = new TreeWalk(r);
  355. walk.reset();
  356. walk.setRecursive(true);
  357. walk.addTree(baseTree);
  358. walk.addTree(commitTree);
  359. walk.setFilter(TreeFilter.ANY_DIFF);
  360. final ByteArrayOutputStream os = new ByteArrayOutputStream();
  361. RawTextComparator cmp = RawTextComparator.DEFAULT;
  362. PatchFormatter df = new PatchFormatter(os);
  363. df.setRepository(r);
  364. df.setDiffComparator(cmp);
  365. df.setDetectRenames(true);
  366. List<DiffEntry> diffs = df.scan(baseTree, commitTree);
  367. if (path != null && path.length() > 0) {
  368. for (DiffEntry diff : diffs) {
  369. if (diff.getNewPath().equalsIgnoreCase(path)) {
  370. df.format(diff);
  371. break;
  372. }
  373. }
  374. } else {
  375. df.format(diffs);
  376. }
  377. String diff = df.getPatch(commit);
  378. df.flush();
  379. return diff;
  380. } catch (Throwable t) {
  381. LOGGER.error("failed to generate commit diff!", t);
  382. }
  383. return null;
  384. }
  385. private static PathModel getPathModel(TreeWalk walk, String basePath, RevCommit commit) {
  386. String name;
  387. long size = 0;
  388. if (basePath == null) {
  389. name = walk.getPathString();
  390. } else {
  391. try {
  392. name = walk.getPathString().substring(basePath.length() + 1);
  393. } catch (Throwable t) {
  394. name = walk.getPathString();
  395. }
  396. }
  397. try {
  398. if (!walk.isSubtree()) {
  399. size = walk.getObjectReader().getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB);
  400. }
  401. } catch (Throwable t) {
  402. LOGGER.error("Failed to retrieve blob size", t);
  403. }
  404. return new PathModel(name, walk.getPathString(), size, walk.getFileMode(0).getBits(), commit.getName());
  405. }
  406. public static String getPermissionsFromMode(int mode) {
  407. if (FileMode.TREE.equals(mode)) {
  408. return "drwxr-xr-x";
  409. } else if (FileMode.REGULAR_FILE.equals(mode)) {
  410. return "-rw-r--r--";
  411. } else if (FileMode.EXECUTABLE_FILE.equals(mode)) {
  412. return "-rwxr-xr-x";
  413. } else if (FileMode.SYMLINK.equals(mode)) {
  414. // FIXME symlink permissions
  415. return "symlink";
  416. } else if (FileMode.GITLINK.equals(mode)) {
  417. // FIXME gitlink permissions
  418. return "gitlink";
  419. } else if (FileMode.MISSING.equals(mode)) {
  420. // FIXME missing permissions
  421. return "missing";
  422. }
  423. return "" + mode;
  424. }
  425. public static boolean isTreeFromMode(int mode) {
  426. return FileMode.TREE.equals(mode);
  427. }
  428. public static List<RevCommit> getRevLog(Repository r, int maxCount) {
  429. return getRevLog(r, Constants.HEAD, 0, maxCount);
  430. }
  431. public static List<RevCommit> getRevLog(Repository r, String objectId, int offset, int maxCount) {
  432. return getRevLog(r, objectId, null, offset, maxCount);
  433. }
  434. public static List<RevCommit> getRevLog(Repository r, String objectId, String path, int offset, int maxCount) {
  435. List<RevCommit> list = new ArrayList<RevCommit>();
  436. try {
  437. if (objectId == null || objectId.trim().length() == 0) {
  438. objectId = Constants.HEAD;
  439. }
  440. RevWalk walk = new RevWalk(r);
  441. ObjectId object = r.resolve(objectId);
  442. walk.markStart(walk.parseCommit(object));
  443. if (!StringUtils.isEmpty(path)) {
  444. TreeFilter filter = AndTreeFilter.create(PathFilterGroup.createFromStrings(Collections.singleton(path)), TreeFilter.ANY_DIFF);
  445. walk.setTreeFilter(filter);
  446. }
  447. Iterable<RevCommit> revlog = walk;
  448. if (offset > 0) {
  449. int count = 0;
  450. for (RevCommit rev : revlog) {
  451. count++;
  452. if (count > offset) {
  453. list.add(rev);
  454. if (maxCount > 0 && list.size() == maxCount) {
  455. break;
  456. }
  457. }
  458. }
  459. } else {
  460. for (RevCommit rev : revlog) {
  461. list.add(rev);
  462. if (maxCount > 0 && list.size() == maxCount) {
  463. break;
  464. }
  465. }
  466. }
  467. walk.dispose();
  468. } catch (Throwable t) {
  469. LOGGER.error("Failed to determine last change", t);
  470. }
  471. return list;
  472. }
  473. public static enum SearchType {
  474. AUTHOR, COMMITTER, COMMIT;
  475. public static SearchType forName(String name) {
  476. for (SearchType type : values()) {
  477. if (type.name().equalsIgnoreCase(name)) {
  478. return type;
  479. }
  480. }
  481. return null;
  482. }
  483. public String toString() {
  484. return name().toLowerCase();
  485. }
  486. }
  487. public static List<RevCommit> searchRevlogs(Repository r, String objectId, String value, final SearchType type, int offset, int maxCount) {
  488. final String lcValue = value.toLowerCase();
  489. List<RevCommit> list = new ArrayList<RevCommit>();
  490. try {
  491. if (objectId == null || objectId.trim().length() == 0) {
  492. objectId = Constants.HEAD;
  493. }
  494. RevWalk walk = new RevWalk(r);
  495. walk.setRevFilter(new RevFilter() {
  496. @Override
  497. public RevFilter clone() {
  498. return this;
  499. }
  500. @Override
  501. public boolean include(RevWalk walker, RevCommit commit) throws StopWalkException, MissingObjectException, IncorrectObjectTypeException, IOException {
  502. switch (type) {
  503. case AUTHOR:
  504. return (commit.getAuthorIdent().getName().toLowerCase().indexOf(lcValue) > -1) || (commit.getAuthorIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
  505. case COMMITTER:
  506. return (commit.getCommitterIdent().getName().toLowerCase().indexOf(lcValue) > -1)|| (commit.getCommitterIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
  507. case COMMIT:
  508. return commit.getFullMessage().toLowerCase().indexOf(lcValue) > -1;
  509. }
  510. return false;
  511. }
  512. });
  513. ObjectId object = r.resolve(objectId);
  514. walk.markStart(walk.parseCommit(object));
  515. Iterable<RevCommit> revlog = walk;
  516. if (offset > 0) {
  517. int count = 0;
  518. for (RevCommit rev : revlog) {
  519. count++;
  520. if (count > offset) {
  521. list.add(rev);
  522. if (maxCount > 0 && list.size() == maxCount) {
  523. break;
  524. }
  525. }
  526. }
  527. } else {
  528. for (RevCommit rev : revlog) {
  529. list.add(rev);
  530. if (maxCount > 0 && list.size() == maxCount) {
  531. break;
  532. }
  533. }
  534. }
  535. walk.dispose();
  536. } catch (Throwable t) {
  537. LOGGER.error("Failed to determine last change", t);
  538. }
  539. return list;
  540. }
  541. public static List<RefModel> getTags(Repository r, int maxCount) {
  542. return getRefs(r, Constants.R_TAGS, maxCount);
  543. }
  544. public static List<RefModel> getLocalBranches(Repository r, int maxCount) {
  545. return getRefs(r, Constants.R_HEADS, maxCount);
  546. }
  547. public static List<RefModel> getRemoteBranches(Repository r, int maxCount) {
  548. return getRefs(r, Constants.R_REMOTES, maxCount);
  549. }
  550. public static List<RefModel> getRefs(Repository r, String refs, int maxCount) {
  551. List<RefModel> list = new ArrayList<RefModel>();
  552. try {
  553. Map<String, Ref> map = r.getRefDatabase().getRefs(refs);
  554. for (String name : map.keySet()) {
  555. Ref ref = map.get(name);
  556. RevCommit commit = getCommit(r, ref.getObjectId().getName());
  557. list.add(new RefModel(name, ref, commit));
  558. }
  559. Collections.sort(list);
  560. Collections.reverse(list);
  561. if (maxCount > 0 && list.size() > maxCount) {
  562. list = new ArrayList<RefModel>(list.subList(0, maxCount));
  563. }
  564. } catch (IOException e) {
  565. LOGGER.error("Failed to retrieve " + refs, e);
  566. }
  567. return list;
  568. }
  569. public static Ref getRef(Repository r, String id) {
  570. try {
  571. Map<String, Ref> map = r.getRefDatabase().getRefs(id);
  572. for (String name : map.keySet()) {
  573. return map.get(name);
  574. }
  575. } catch (IOException e) {
  576. LOGGER.error("Failed to retrieve ref " + id, e);
  577. }
  578. return null;
  579. }
  580. public static Date getCommitDate(RevCommit commit) {
  581. return new Date(commit.getCommitTime() * 1000l);
  582. }
  583. public static String getDisplayName(PersonIdent person) {
  584. final StringBuilder r = new StringBuilder();
  585. r.append(person.getName());
  586. r.append(" <");
  587. r.append(person.getEmailAddress());
  588. r.append(">");
  589. return r.toString();
  590. }
  591. public static String getRepositoryDescription(Repository r) {
  592. File dir = r.getDirectory();
  593. if (dir.exists()) {
  594. File description = new File(dir, "description");
  595. if (description.exists() && description.length() > 0) {
  596. RandomAccessFile raf = null;
  597. try {
  598. raf = new RandomAccessFile(description, "r");
  599. byte[] buffer = new byte[(int) description.length()];
  600. raf.readFully(buffer);
  601. return new String(buffer);
  602. } catch (Throwable t) {
  603. } finally {
  604. try {
  605. raf.close();
  606. } catch (Throwable t) {
  607. }
  608. }
  609. }
  610. }
  611. return "";
  612. }
  613. public static String getRepositoryOwner(Repository r) {
  614. StoredConfig c = readConfig(r);
  615. if (c == null) {
  616. return "";
  617. }
  618. String o = c.getString("gitweb", null, "owner");
  619. return o == null ? "" : o;
  620. }
  621. private static StoredConfig readConfig(Repository r) {
  622. StoredConfig c = r.getConfig();
  623. if (c != null) {
  624. try {
  625. c.load();
  626. } catch (ConfigInvalidException cex) {
  627. LOGGER.error("Repository configuration is invalid!", cex);
  628. } catch (IOException cex) {
  629. LOGGER.error("Could not open repository configuration!", cex);
  630. }
  631. return c;
  632. }
  633. return null;
  634. }
  635. public static List<Metric> getDateMetrics(Repository r) {
  636. final List<RefModel> tags = getTags(r, -1);
  637. final Map<String, Metric> map = new HashMap<String, Metric>();
  638. try {
  639. DateFormat df = new SimpleDateFormat("yyyy-MM");
  640. RevWalk walk = new RevWalk(r);
  641. ObjectId object = r.resolve(Constants.HEAD);
  642. walk.markStart(walk.parseCommit(object));
  643. Iterable<RevCommit> revlog = walk;
  644. for (RevCommit rev : revlog) {
  645. Date d = getCommitDate(rev);
  646. String p = df.format(d);
  647. if (!map.containsKey(p))
  648. map.put(p, new Metric(p));
  649. map.get(p).count++;
  650. }
  651. } catch (Throwable t) {
  652. LOGGER.error("Failed to mine log history for metrics", t);
  653. }
  654. List<String> keys = new ArrayList<String>(map.keySet());
  655. Collections.sort(keys);
  656. List<Metric> metrics = new ArrayList<Metric>();
  657. for (String key : keys) {
  658. metrics.add(map.get(key));
  659. }
  660. return metrics;
  661. }
  662. public static RefModel getTicketsBranch(Repository r) {
  663. RefModel ticgitBranch = null;
  664. try {
  665. // search for ticgit branch in local heads
  666. for (RefModel ref : getLocalBranches(r, -1)) {
  667. if (ref.getDisplayName().endsWith("ticgit")) {
  668. ticgitBranch = ref;
  669. break;
  670. }
  671. }
  672. // search for ticgit branch in remote heads
  673. if (ticgitBranch == null) {
  674. for (RefModel ref : getRemoteBranches(r, -1)) {
  675. if (ref.getDisplayName().endsWith("ticgit")) {
  676. ticgitBranch = ref;
  677. break;
  678. }
  679. }
  680. }
  681. } catch (Throwable t) {
  682. LOGGER.error("Failed to find ticgit branch!", t);
  683. }
  684. return ticgitBranch;
  685. }
  686. public static List<TicketModel> getTickets(Repository r) {
  687. RefModel ticgitBranch = getTicketsBranch(r);
  688. List<PathModel> paths = getFilesInPath(r, null, ticgitBranch.getCommit());
  689. List<TicketModel> tickets = new ArrayList<TicketModel>();
  690. for (PathModel ticketFolder : paths) {
  691. if (ticketFolder.isTree()) {
  692. try {
  693. TicketModel t = new TicketModel(ticketFolder.name);
  694. readTicketContents(r, ticgitBranch, t);
  695. tickets.add(t);
  696. } catch (Throwable t) {
  697. LOGGER.error("Failed to get a ticket!", t);
  698. }
  699. }
  700. }
  701. Collections.sort(tickets);
  702. Collections.reverse(tickets);
  703. return tickets;
  704. }
  705. public static TicketModel getTicket(Repository r, String ticketFolder) {
  706. RefModel ticketsBranch = getTicketsBranch(r);
  707. if (ticketsBranch != null) {
  708. try {
  709. TicketModel ticket = new TicketModel(ticketFolder);
  710. readTicketContents(r, ticketsBranch, ticket);
  711. return ticket;
  712. } catch (Throwable t) {
  713. LOGGER.error("Failed to get ticket " + ticketFolder, t);
  714. }
  715. }
  716. return null;
  717. }
  718. private static void readTicketContents(Repository r, RefModel ticketsBranch, TicketModel ticket) {
  719. List<PathModel> ticketFiles = getFilesInPath(r, ticket.name, ticketsBranch.getCommit());
  720. for (PathModel file : ticketFiles) {
  721. String content = getRawContentAsString(r, ticketsBranch.getCommit(), file.path).trim();
  722. if (file.name.equals("TICKET_ID")) {
  723. ticket.id = content;
  724. } else if (file.name.equals("TITLE")) {
  725. ticket.title = content;
  726. } else {
  727. String[] chunks = file.name.split("_");
  728. if (chunks[0].equals("ASSIGNED")) {
  729. ticket.handler = content;
  730. } else if (chunks[0].equals("COMMENT")) {
  731. try {
  732. Comment c = new Comment(file.name, content);
  733. ticket.comments.add(c);
  734. } catch (ParseException e) {
  735. e.printStackTrace();
  736. }
  737. } else if (chunks[0].equals("TAG")) {
  738. if (content.startsWith("TAG_")) {
  739. ticket.tags.add(content.substring(4));
  740. } else {
  741. ticket.tags.add(content);
  742. }
  743. } else if (chunks[0].equals("STATE")) {
  744. ticket.state = content;
  745. }
  746. }
  747. }
  748. Collections.sort(ticket.comments);
  749. }
  750. public static String getTicketContent(Repository r, String filePath) {
  751. RefModel ticketsBranch = getTicketsBranch(r);
  752. if (ticketsBranch != null) {
  753. return getRawContentAsString(r, ticketsBranch.getCommit(), filePath);
  754. }
  755. return "";
  756. }
  757. }