Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

JGitUtils.java 25KB

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. }