Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

JGitUtils.java 25KB

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