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 60KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900
  1. /*
  2. * Copyright 2011 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.utils;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.text.DecimalFormat;
  22. import java.text.MessageFormat;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.Collections;
  26. import java.util.Date;
  27. import java.util.HashMap;
  28. import java.util.Iterator;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.Map.Entry;
  32. import java.util.regex.Pattern;
  33. import org.eclipse.jgit.api.CloneCommand;
  34. import org.eclipse.jgit.api.FetchCommand;
  35. import org.eclipse.jgit.api.Git;
  36. import org.eclipse.jgit.api.TagCommand;
  37. import org.eclipse.jgit.api.errors.GitAPIException;
  38. import org.eclipse.jgit.diff.DiffEntry;
  39. import org.eclipse.jgit.diff.DiffEntry.ChangeType;
  40. import org.eclipse.jgit.diff.DiffFormatter;
  41. import org.eclipse.jgit.diff.RawTextComparator;
  42. import org.eclipse.jgit.errors.ConfigInvalidException;
  43. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  44. import org.eclipse.jgit.errors.MissingObjectException;
  45. import org.eclipse.jgit.errors.StopWalkException;
  46. import org.eclipse.jgit.lib.BlobBasedConfig;
  47. import org.eclipse.jgit.lib.CommitBuilder;
  48. import org.eclipse.jgit.lib.Constants;
  49. import org.eclipse.jgit.lib.FileMode;
  50. import org.eclipse.jgit.lib.ObjectId;
  51. import org.eclipse.jgit.lib.ObjectInserter;
  52. import org.eclipse.jgit.lib.ObjectLoader;
  53. import org.eclipse.jgit.lib.PersonIdent;
  54. import org.eclipse.jgit.lib.Ref;
  55. import org.eclipse.jgit.lib.RefUpdate;
  56. import org.eclipse.jgit.lib.RefUpdate.Result;
  57. import org.eclipse.jgit.lib.Repository;
  58. import org.eclipse.jgit.lib.RepositoryCache.FileKey;
  59. import org.eclipse.jgit.lib.TreeFormatter;
  60. import org.eclipse.jgit.revwalk.RevBlob;
  61. import org.eclipse.jgit.revwalk.RevCommit;
  62. import org.eclipse.jgit.revwalk.RevObject;
  63. import org.eclipse.jgit.revwalk.RevSort;
  64. import org.eclipse.jgit.revwalk.RevTree;
  65. import org.eclipse.jgit.revwalk.RevWalk;
  66. import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
  67. import org.eclipse.jgit.revwalk.filter.RevFilter;
  68. import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
  69. import org.eclipse.jgit.transport.CredentialsProvider;
  70. import org.eclipse.jgit.transport.FetchResult;
  71. import org.eclipse.jgit.transport.RefSpec;
  72. import org.eclipse.jgit.treewalk.TreeWalk;
  73. import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
  74. import org.eclipse.jgit.treewalk.filter.OrTreeFilter;
  75. import org.eclipse.jgit.treewalk.filter.PathFilter;
  76. import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
  77. import org.eclipse.jgit.treewalk.filter.PathSuffixFilter;
  78. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  79. import org.eclipse.jgit.util.FS;
  80. import org.eclipse.jgit.util.io.DisabledOutputStream;
  81. import org.slf4j.Logger;
  82. import org.slf4j.LoggerFactory;
  83. import com.gitblit.models.GitNote;
  84. import com.gitblit.models.PathModel;
  85. import com.gitblit.models.PathModel.PathChangeModel;
  86. import com.gitblit.models.RefModel;
  87. import com.gitblit.models.SubmoduleModel;
  88. /**
  89. * Collection of static methods for retrieving information from a repository.
  90. *
  91. * @author James Moger
  92. *
  93. */
  94. public class JGitUtils {
  95. static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
  96. /**
  97. * Log an error message and exception.
  98. *
  99. * @param t
  100. * @param repository
  101. * if repository is not null it MUST be the {0} parameter in the
  102. * pattern.
  103. * @param pattern
  104. * @param objects
  105. */
  106. private static void error(Throwable t, Repository repository, String pattern, Object... objects) {
  107. List<Object> parameters = new ArrayList<Object>();
  108. if (objects != null && objects.length > 0) {
  109. for (Object o : objects) {
  110. parameters.add(o);
  111. }
  112. }
  113. if (repository != null) {
  114. parameters.add(0, repository.getDirectory().getAbsolutePath());
  115. }
  116. LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t);
  117. }
  118. /**
  119. * Returns the displayable name of the person in the form "Real Name <email
  120. * address>". If the email address is empty, just "Real Name" is returned.
  121. *
  122. * @param person
  123. * @return "Real Name <email address>" or "Real Name"
  124. */
  125. public static String getDisplayName(PersonIdent person) {
  126. if (StringUtils.isEmpty(person.getEmailAddress())) {
  127. return person.getName();
  128. }
  129. final StringBuilder r = new StringBuilder();
  130. r.append(person.getName());
  131. r.append(" <");
  132. r.append(person.getEmailAddress());
  133. r.append('>');
  134. return r.toString().trim();
  135. }
  136. /**
  137. * Encapsulates the result of cloning or pulling from a repository.
  138. */
  139. public static class CloneResult {
  140. public String name;
  141. public FetchResult fetchResult;
  142. public boolean createdRepository;
  143. }
  144. /**
  145. * Clone or Fetch a repository. If the local repository does not exist,
  146. * clone is called. If the repository does exist, fetch is called. By
  147. * default the clone/fetch retrieves the remote heads, tags, and notes.
  148. *
  149. * @param repositoriesFolder
  150. * @param name
  151. * @param fromUrl
  152. * @return CloneResult
  153. * @throws Exception
  154. */
  155. public static CloneResult cloneRepository(File repositoriesFolder, String name, String fromUrl)
  156. throws Exception {
  157. return cloneRepository(repositoriesFolder, name, fromUrl, true, null);
  158. }
  159. /**
  160. * Clone or Fetch a repository. If the local repository does not exist,
  161. * clone is called. If the repository does exist, fetch is called. By
  162. * default the clone/fetch retrieves the remote heads, tags, and notes.
  163. *
  164. * @param repositoriesFolder
  165. * @param name
  166. * @param fromUrl
  167. * @param bare
  168. * @param credentialsProvider
  169. * @return CloneResult
  170. * @throws Exception
  171. */
  172. public static CloneResult cloneRepository(File repositoriesFolder, String name, String fromUrl,
  173. boolean bare, CredentialsProvider credentialsProvider) throws Exception {
  174. CloneResult result = new CloneResult();
  175. if (bare) {
  176. // bare repository, ensure .git suffix
  177. if (!name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) {
  178. name += Constants.DOT_GIT_EXT;
  179. }
  180. } else {
  181. // normal repository, strip .git suffix
  182. if (name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) {
  183. name = name.substring(0, name.indexOf(Constants.DOT_GIT_EXT));
  184. }
  185. }
  186. result.name = name;
  187. File folder = new File(repositoriesFolder, name);
  188. if (folder.exists()) {
  189. File gitDir = FileKey.resolve(new File(repositoriesFolder, name), FS.DETECTED);
  190. Repository repository = new FileRepositoryBuilder().setGitDir(gitDir).build();
  191. result.fetchResult = fetchRepository(credentialsProvider, repository);
  192. repository.close();
  193. } else {
  194. CloneCommand clone = new CloneCommand();
  195. clone.setBare(bare);
  196. clone.setCloneAllBranches(true);
  197. clone.setURI(fromUrl);
  198. clone.setDirectory(folder);
  199. if (credentialsProvider != null) {
  200. clone.setCredentialsProvider(credentialsProvider);
  201. }
  202. Repository repository = clone.call().getRepository();
  203. // Now we have to fetch because CloneCommand doesn't fetch
  204. // refs/notes nor does it allow manual RefSpec.
  205. result.createdRepository = true;
  206. result.fetchResult = fetchRepository(credentialsProvider, repository);
  207. repository.close();
  208. }
  209. return result;
  210. }
  211. /**
  212. * Fetch updates from the remote repository. If refSpecs is unspecifed,
  213. * remote heads, tags, and notes are retrieved.
  214. *
  215. * @param credentialsProvider
  216. * @param repository
  217. * @param refSpecs
  218. * @return FetchResult
  219. * @throws Exception
  220. */
  221. public static FetchResult fetchRepository(CredentialsProvider credentialsProvider,
  222. Repository repository, RefSpec... refSpecs) throws Exception {
  223. Git git = new Git(repository);
  224. FetchCommand fetch = git.fetch();
  225. List<RefSpec> specs = new ArrayList<RefSpec>();
  226. if (refSpecs == null || refSpecs.length == 0) {
  227. specs.add(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
  228. specs.add(new RefSpec("+refs/tags/*:refs/tags/*"));
  229. specs.add(new RefSpec("+refs/notes/*:refs/notes/*"));
  230. } else {
  231. specs.addAll(Arrays.asList(refSpecs));
  232. }
  233. if (credentialsProvider != null) {
  234. fetch.setCredentialsProvider(credentialsProvider);
  235. }
  236. fetch.setRefSpecs(specs);
  237. FetchResult fetchRes = fetch.call();
  238. return fetchRes;
  239. }
  240. /**
  241. * Creates a bare repository.
  242. *
  243. * @param repositoriesFolder
  244. * @param name
  245. * @return Repository
  246. */
  247. public static Repository createRepository(File repositoriesFolder, String name) {
  248. try {
  249. Git git = Git.init().setDirectory(new File(repositoriesFolder, name)).setBare(true).call();
  250. return git.getRepository();
  251. } catch (GitAPIException e) {
  252. throw new RuntimeException(e);
  253. }
  254. }
  255. /**
  256. * Returns a list of repository names in the specified folder.
  257. *
  258. * @param repositoriesFolder
  259. * @param onlyBare
  260. * if true, only bare repositories repositories are listed. If
  261. * false all repositories are included.
  262. * @param searchSubfolders
  263. * recurse into subfolders to find grouped repositories
  264. * @param depth
  265. * optional recursion depth, -1 = infinite recursion
  266. * @param exclusions
  267. * list of regex exclusions for matching to folder names
  268. * @return list of repository names
  269. */
  270. public static List<String> getRepositoryList(File repositoriesFolder, boolean onlyBare,
  271. boolean searchSubfolders, int depth, List<String> exclusions) {
  272. List<String> list = new ArrayList<String>();
  273. if (repositoriesFolder == null || !repositoriesFolder.exists()) {
  274. return list;
  275. }
  276. List<Pattern> patterns = new ArrayList<Pattern>();
  277. if (!ArrayUtils.isEmpty(exclusions)) {
  278. for (String regex : exclusions) {
  279. patterns.add(Pattern.compile(regex));
  280. }
  281. }
  282. list.addAll(getRepositoryList(repositoriesFolder.getAbsolutePath(), repositoriesFolder,
  283. onlyBare, searchSubfolders, depth, patterns));
  284. StringUtils.sortRepositorynames(list);
  285. list.remove(".git"); // issue-256
  286. return list;
  287. }
  288. /**
  289. * Recursive function to find git repositories.
  290. *
  291. * @param basePath
  292. * basePath is stripped from the repository name as repositories
  293. * are relative to this path
  294. * @param searchFolder
  295. * @param onlyBare
  296. * if true only bare repositories will be listed. if false all
  297. * repositories are included.
  298. * @param searchSubfolders
  299. * recurse into subfolders to find grouped repositories
  300. * @param depth
  301. * recursion depth, -1 = infinite recursion
  302. * @param patterns
  303. * list of regex patterns for matching to folder names
  304. * @return
  305. */
  306. private static List<String> getRepositoryList(String basePath, File searchFolder,
  307. boolean onlyBare, boolean searchSubfolders, int depth, List<Pattern> patterns) {
  308. File baseFile = new File(basePath);
  309. List<String> list = new ArrayList<String>();
  310. if (depth == 0) {
  311. return list;
  312. }
  313. int nextDepth = (depth == -1) ? -1 : depth - 1;
  314. for (File file : searchFolder.listFiles()) {
  315. if (file.isDirectory()) {
  316. boolean exclude = false;
  317. for (Pattern pattern : patterns) {
  318. String path = FileUtils.getRelativePath(baseFile, file).replace('\\', '/');
  319. if (pattern.matcher(path).matches()) {
  320. LOGGER.debug(MessageFormat.format("excluding {0} because of rule {1}", path, pattern.pattern()));
  321. exclude = true;
  322. break;
  323. }
  324. }
  325. if (exclude) {
  326. // skip to next file
  327. continue;
  328. }
  329. File gitDir = FileKey.resolve(new File(searchFolder, file.getName()), FS.DETECTED);
  330. if (gitDir != null) {
  331. if (onlyBare && gitDir.getName().equals(".git")) {
  332. continue;
  333. }
  334. if (gitDir.equals(file) || gitDir.getParentFile().equals(file)) {
  335. // determine repository name relative to base path
  336. String repository = FileUtils.getRelativePath(baseFile, file);
  337. list.add(repository);
  338. } else if (searchSubfolders && file.canRead()) {
  339. // look for repositories in subfolders
  340. list.addAll(getRepositoryList(basePath, file, onlyBare, searchSubfolders,
  341. nextDepth, patterns));
  342. }
  343. } else if (searchSubfolders && file.canRead()) {
  344. // look for repositories in subfolders
  345. list.addAll(getRepositoryList(basePath, file, onlyBare, searchSubfolders,
  346. nextDepth, patterns));
  347. }
  348. }
  349. }
  350. return list;
  351. }
  352. /**
  353. * Returns the first commit on a branch. If the repository does not exist or
  354. * is empty, null is returned.
  355. *
  356. * @param repository
  357. * @param branch
  358. * if unspecified, HEAD is assumed.
  359. * @return RevCommit
  360. */
  361. public static RevCommit getFirstCommit(Repository repository, String branch) {
  362. if (!hasCommits(repository)) {
  363. return null;
  364. }
  365. RevCommit commit = null;
  366. try {
  367. // resolve branch
  368. ObjectId branchObject;
  369. if (StringUtils.isEmpty(branch)) {
  370. branchObject = getDefaultBranch(repository);
  371. } else {
  372. branchObject = repository.resolve(branch);
  373. }
  374. RevWalk walk = new RevWalk(repository);
  375. walk.sort(RevSort.REVERSE);
  376. RevCommit head = walk.parseCommit(branchObject);
  377. walk.markStart(head);
  378. commit = walk.next();
  379. walk.dispose();
  380. } catch (Throwable t) {
  381. error(t, repository, "{0} failed to determine first commit");
  382. }
  383. return commit;
  384. }
  385. /**
  386. * Returns the date of the first commit on a branch. If the repository does
  387. * not exist, Date(0) is returned. If the repository does exist bit is
  388. * empty, the last modified date of the repository folder is returned.
  389. *
  390. * @param repository
  391. * @param branch
  392. * if unspecified, HEAD is assumed.
  393. * @return Date of the first commit on a branch
  394. */
  395. public static Date getFirstChange(Repository repository, String branch) {
  396. RevCommit commit = getFirstCommit(repository, branch);
  397. if (commit == null) {
  398. if (repository == null || !repository.getDirectory().exists()) {
  399. return new Date(0);
  400. }
  401. // fresh repository
  402. return new Date(repository.getDirectory().lastModified());
  403. }
  404. return getCommitDate(commit);
  405. }
  406. /**
  407. * Determine if a repository has any commits. This is determined by checking
  408. * the for loose and packed objects.
  409. *
  410. * @param repository
  411. * @return true if the repository has commits
  412. */
  413. public static boolean hasCommits(Repository repository) {
  414. if (repository != null && repository.getDirectory().exists()) {
  415. return (new File(repository.getDirectory(), "objects").list().length > 2)
  416. || (new File(repository.getDirectory(), "objects/pack").list().length > 0);
  417. }
  418. return false;
  419. }
  420. /**
  421. * Returns the date of the most recent commit on a branch. If the repository
  422. * does not exist Date(0) is returned. If it does exist but is empty, the
  423. * last modified date of the repository folder is returned.
  424. *
  425. * @param repository
  426. * @return
  427. */
  428. public static Date getLastChange(Repository repository) {
  429. if (!hasCommits(repository)) {
  430. // null repository
  431. if (repository == null) {
  432. return new Date(0);
  433. }
  434. // fresh repository
  435. return new Date(repository.getDirectory().lastModified());
  436. }
  437. List<RefModel> branchModels = getLocalBranches(repository, true, -1);
  438. if (branchModels.size() > 0) {
  439. // find most recent branch update
  440. Date lastChange = new Date(0);
  441. for (RefModel branchModel : branchModels) {
  442. if (branchModel.getDate().after(lastChange)) {
  443. lastChange = branchModel.getDate();
  444. }
  445. }
  446. return lastChange;
  447. }
  448. // default to the repository folder modification date
  449. return new Date(repository.getDirectory().lastModified());
  450. }
  451. /**
  452. * Retrieves a Java Date from a Git commit.
  453. *
  454. * @param commit
  455. * @return date of the commit or Date(0) if the commit is null
  456. */
  457. public static Date getCommitDate(RevCommit commit) {
  458. if (commit == null) {
  459. return new Date(0);
  460. }
  461. return new Date(commit.getCommitTime() * 1000L);
  462. }
  463. /**
  464. * Retrieves a Java Date from a Git commit.
  465. *
  466. * @param commit
  467. * @return date of the commit or Date(0) if the commit is null
  468. */
  469. public static Date getAuthorDate(RevCommit commit) {
  470. if (commit == null) {
  471. return new Date(0);
  472. }
  473. return commit.getAuthorIdent().getWhen();
  474. }
  475. /**
  476. * Returns the specified commit from the repository. If the repository does
  477. * not exist or is empty, null is returned.
  478. *
  479. * @param repository
  480. * @param objectId
  481. * if unspecified, HEAD is assumed.
  482. * @return RevCommit
  483. */
  484. public static RevCommit getCommit(Repository repository, String objectId) {
  485. if (!hasCommits(repository)) {
  486. return null;
  487. }
  488. RevCommit commit = null;
  489. try {
  490. // resolve object id
  491. ObjectId branchObject;
  492. if (StringUtils.isEmpty(objectId)) {
  493. branchObject = getDefaultBranch(repository);
  494. } else {
  495. branchObject = repository.resolve(objectId);
  496. }
  497. RevWalk walk = new RevWalk(repository);
  498. RevCommit rev = walk.parseCommit(branchObject);
  499. commit = rev;
  500. walk.dispose();
  501. } catch (Throwable t) {
  502. error(t, repository, "{0} failed to get commit {1}", objectId);
  503. }
  504. return commit;
  505. }
  506. /**
  507. * Retrieves the raw byte content of a file in the specified tree.
  508. *
  509. * @param repository
  510. * @param tree
  511. * if null, the RevTree from HEAD is assumed.
  512. * @param path
  513. * @return content as a byte []
  514. */
  515. public static byte[] getByteContent(Repository repository, RevTree tree, final String path, boolean throwError) {
  516. RevWalk rw = new RevWalk(repository);
  517. TreeWalk tw = new TreeWalk(repository);
  518. tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path)));
  519. byte[] content = null;
  520. try {
  521. if (tree == null) {
  522. ObjectId object = getDefaultBranch(repository);
  523. RevCommit commit = rw.parseCommit(object);
  524. tree = commit.getTree();
  525. }
  526. tw.reset(tree);
  527. while (tw.next()) {
  528. if (tw.isSubtree() && !path.equals(tw.getPathString())) {
  529. tw.enterSubtree();
  530. continue;
  531. }
  532. ObjectId entid = tw.getObjectId(0);
  533. FileMode entmode = tw.getFileMode(0);
  534. if (entmode != FileMode.GITLINK) {
  535. RevObject ro = rw.lookupAny(entid, entmode.getObjectType());
  536. rw.parseBody(ro);
  537. ByteArrayOutputStream os = new ByteArrayOutputStream();
  538. ObjectLoader ldr = repository.open(ro.getId(), Constants.OBJ_BLOB);
  539. byte[] tmp = new byte[4096];
  540. InputStream in = ldr.openStream();
  541. int n;
  542. while ((n = in.read(tmp)) > 0) {
  543. os.write(tmp, 0, n);
  544. }
  545. in.close();
  546. content = os.toByteArray();
  547. }
  548. }
  549. } catch (Throwable t) {
  550. if (throwError) {
  551. error(t, repository, "{0} can't find {1} in tree {2}", path, tree.name());
  552. }
  553. } finally {
  554. rw.dispose();
  555. tw.release();
  556. }
  557. return content;
  558. }
  559. /**
  560. * Returns the UTF-8 string content of a file in the specified tree.
  561. *
  562. * @param repository
  563. * @param tree
  564. * if null, the RevTree from HEAD is assumed.
  565. * @param blobPath
  566. * @param charsets optional
  567. * @return UTF-8 string content
  568. */
  569. public static String getStringContent(Repository repository, RevTree tree, String blobPath, String... charsets) {
  570. byte[] content = getByteContent(repository, tree, blobPath, true);
  571. if (content == null) {
  572. return null;
  573. }
  574. return StringUtils.decodeString(content, charsets);
  575. }
  576. /**
  577. * Gets the raw byte content of the specified blob object.
  578. *
  579. * @param repository
  580. * @param objectId
  581. * @return byte [] blob content
  582. */
  583. public static byte[] getByteContent(Repository repository, String objectId) {
  584. RevWalk rw = new RevWalk(repository);
  585. byte[] content = null;
  586. try {
  587. RevBlob blob = rw.lookupBlob(ObjectId.fromString(objectId));
  588. rw.parseBody(blob);
  589. ByteArrayOutputStream os = new ByteArrayOutputStream();
  590. ObjectLoader ldr = repository.open(blob.getId(), Constants.OBJ_BLOB);
  591. byte[] tmp = new byte[4096];
  592. InputStream in = ldr.openStream();
  593. int n;
  594. while ((n = in.read(tmp)) > 0) {
  595. os.write(tmp, 0, n);
  596. }
  597. in.close();
  598. content = os.toByteArray();
  599. } catch (Throwable t) {
  600. error(t, repository, "{0} can't find blob {1}", objectId);
  601. } finally {
  602. rw.dispose();
  603. }
  604. return content;
  605. }
  606. /**
  607. * Gets the UTF-8 string content of the blob specified by objectId.
  608. *
  609. * @param repository
  610. * @param objectId
  611. * @param charsets optional
  612. * @return UTF-8 string content
  613. */
  614. public static String getStringContent(Repository repository, String objectId, String... charsets) {
  615. byte[] content = getByteContent(repository, objectId);
  616. if (content == null) {
  617. return null;
  618. }
  619. return StringUtils.decodeString(content, charsets);
  620. }
  621. /**
  622. * Returns the list of files in the specified folder at the specified
  623. * commit. If the repository does not exist or is empty, an empty list is
  624. * returned.
  625. *
  626. * @param repository
  627. * @param path
  628. * if unspecified, root folder is assumed.
  629. * @param commit
  630. * if null, HEAD is assumed.
  631. * @return list of files in specified path
  632. */
  633. public static List<PathModel> getFilesInPath(Repository repository, String path,
  634. RevCommit commit) {
  635. List<PathModel> list = new ArrayList<PathModel>();
  636. if (!hasCommits(repository)) {
  637. return list;
  638. }
  639. if (commit == null) {
  640. commit = getCommit(repository, null);
  641. }
  642. final TreeWalk tw = new TreeWalk(repository);
  643. try {
  644. tw.addTree(commit.getTree());
  645. if (!StringUtils.isEmpty(path)) {
  646. PathFilter f = PathFilter.create(path);
  647. tw.setFilter(f);
  648. tw.setRecursive(false);
  649. boolean foundFolder = false;
  650. while (tw.next()) {
  651. if (!foundFolder && tw.isSubtree()) {
  652. tw.enterSubtree();
  653. }
  654. if (tw.getPathString().equals(path)) {
  655. foundFolder = true;
  656. continue;
  657. }
  658. if (foundFolder) {
  659. list.add(getPathModel(tw, path, commit));
  660. }
  661. }
  662. } else {
  663. tw.setRecursive(false);
  664. while (tw.next()) {
  665. list.add(getPathModel(tw, null, commit));
  666. }
  667. }
  668. } catch (IOException e) {
  669. error(e, repository, "{0} failed to get files for commit {1}", commit.getName());
  670. } finally {
  671. tw.release();
  672. }
  673. Collections.sort(list);
  674. return list;
  675. }
  676. /**
  677. * Returns the list of files changed in a specified commit. If the
  678. * repository does not exist or is empty, an empty list is returned.
  679. *
  680. * @param repository
  681. * @param commit
  682. * if null, HEAD is assumed.
  683. * @return list of files changed in a commit
  684. */
  685. public static List<PathChangeModel> getFilesInCommit(Repository repository, RevCommit commit) {
  686. List<PathChangeModel> list = new ArrayList<PathChangeModel>();
  687. if (!hasCommits(repository)) {
  688. return list;
  689. }
  690. RevWalk rw = new RevWalk(repository);
  691. try {
  692. if (commit == null) {
  693. ObjectId object = getDefaultBranch(repository);
  694. commit = rw.parseCommit(object);
  695. }
  696. if (commit.getParentCount() == 0) {
  697. TreeWalk tw = new TreeWalk(repository);
  698. tw.reset();
  699. tw.setRecursive(true);
  700. tw.addTree(commit.getTree());
  701. while (tw.next()) {
  702. list.add(new PathChangeModel(tw.getPathString(), tw.getPathString(), 0, tw
  703. .getRawMode(0), tw.getObjectId(0).getName(), commit.getId().getName(),
  704. ChangeType.ADD));
  705. }
  706. tw.release();
  707. } else {
  708. RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
  709. DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);
  710. df.setRepository(repository);
  711. df.setDiffComparator(RawTextComparator.DEFAULT);
  712. df.setDetectRenames(true);
  713. List<DiffEntry> diffs = df.scan(parent.getTree(), commit.getTree());
  714. for (DiffEntry diff : diffs) {
  715. String objectId = diff.getNewId().name();
  716. if (diff.getChangeType().equals(ChangeType.DELETE)) {
  717. list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff
  718. .getNewMode().getBits(), objectId, commit.getId().getName(), diff
  719. .getChangeType()));
  720. } else if (diff.getChangeType().equals(ChangeType.RENAME)) {
  721. list.add(new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff
  722. .getNewMode().getBits(), objectId, commit.getId().getName(), diff
  723. .getChangeType()));
  724. } else {
  725. list.add(new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff
  726. .getNewMode().getBits(), objectId, commit.getId().getName(), diff
  727. .getChangeType()));
  728. }
  729. }
  730. }
  731. } catch (Throwable t) {
  732. error(t, repository, "{0} failed to determine files in commit!");
  733. } finally {
  734. rw.dispose();
  735. }
  736. return list;
  737. }
  738. /**
  739. * Returns the list of files changed in a specified commit. If the
  740. * repository does not exist or is empty, an empty list is returned.
  741. *
  742. * @param repository
  743. * @param startCommit
  744. * earliest commit
  745. * @param endCommit
  746. * most recent commit. if null, HEAD is assumed.
  747. * @return list of files changed in a commit range
  748. */
  749. public static List<PathChangeModel> getFilesInRange(Repository repository, RevCommit startCommit, RevCommit endCommit) {
  750. List<PathChangeModel> list = new ArrayList<PathChangeModel>();
  751. if (!hasCommits(repository)) {
  752. return list;
  753. }
  754. try {
  755. DiffFormatter df = new DiffFormatter(null);
  756. df.setRepository(repository);
  757. df.setDiffComparator(RawTextComparator.DEFAULT);
  758. df.setDetectRenames(true);
  759. List<DiffEntry> diffEntries = df.scan(startCommit.getTree(), endCommit.getTree());
  760. for (DiffEntry diff : diffEntries) {
  761. if (diff.getChangeType().equals(ChangeType.DELETE)) {
  762. list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff
  763. .getNewMode().getBits(), diff.getOldId().name(), null, diff
  764. .getChangeType()));
  765. } else if (diff.getChangeType().equals(ChangeType.RENAME)) {
  766. list.add(new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff
  767. .getNewMode().getBits(), diff.getNewId().name(), null, diff
  768. .getChangeType()));
  769. } else {
  770. list.add(new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff
  771. .getNewMode().getBits(), diff.getNewId().name(), null, diff
  772. .getChangeType()));
  773. }
  774. }
  775. Collections.sort(list);
  776. } catch (Throwable t) {
  777. error(t, repository, "{0} failed to determine files in range {1}..{2}!", startCommit, endCommit);
  778. }
  779. return list;
  780. }
  781. /**
  782. * Returns the list of files in the repository on the default branch that
  783. * match one of the specified extensions. This is a CASE-SENSITIVE search.
  784. * If the repository does not exist or is empty, an empty list is returned.
  785. *
  786. * @param repository
  787. * @param extensions
  788. * @return list of files in repository with a matching extension
  789. */
  790. public static List<PathModel> getDocuments(Repository repository, List<String> extensions) {
  791. return getDocuments(repository, extensions, null);
  792. }
  793. /**
  794. * Returns the list of files in the repository in the specified commit that
  795. * match one of the specified extensions. This is a CASE-SENSITIVE search.
  796. * If the repository does not exist or is empty, an empty list is returned.
  797. *
  798. * @param repository
  799. * @param extensions
  800. * @param objectId
  801. * @return list of files in repository with a matching extension
  802. */
  803. public static List<PathModel> getDocuments(Repository repository, List<String> extensions,
  804. String objectId) {
  805. List<PathModel> list = new ArrayList<PathModel>();
  806. if (!hasCommits(repository)) {
  807. return list;
  808. }
  809. RevCommit commit = getCommit(repository, objectId);
  810. final TreeWalk tw = new TreeWalk(repository);
  811. try {
  812. tw.addTree(commit.getTree());
  813. if (extensions != null && extensions.size() > 0) {
  814. List<TreeFilter> suffixFilters = new ArrayList<TreeFilter>();
  815. for (String extension : extensions) {
  816. if (extension.charAt(0) == '.') {
  817. suffixFilters.add(PathSuffixFilter.create("\\" + extension));
  818. } else {
  819. // escape the . since this is a regexp filter
  820. suffixFilters.add(PathSuffixFilter.create("\\." + extension));
  821. }
  822. }
  823. TreeFilter filter;
  824. if (suffixFilters.size() == 1) {
  825. filter = suffixFilters.get(0);
  826. } else {
  827. filter = OrTreeFilter.create(suffixFilters);
  828. }
  829. tw.setFilter(filter);
  830. tw.setRecursive(true);
  831. }
  832. while (tw.next()) {
  833. list.add(getPathModel(tw, null, commit));
  834. }
  835. } catch (IOException e) {
  836. error(e, repository, "{0} failed to get documents for commit {1}", commit.getName());
  837. } finally {
  838. tw.release();
  839. }
  840. Collections.sort(list);
  841. return list;
  842. }
  843. /**
  844. * Returns a path model of the current file in the treewalk.
  845. *
  846. * @param tw
  847. * @param basePath
  848. * @param commit
  849. * @return a path model of the current file in the treewalk
  850. */
  851. private static PathModel getPathModel(TreeWalk tw, String basePath, RevCommit commit) {
  852. String name;
  853. long size = 0;
  854. if (StringUtils.isEmpty(basePath)) {
  855. name = tw.getPathString();
  856. } else {
  857. name = tw.getPathString().substring(basePath.length() + 1);
  858. }
  859. ObjectId objectId = tw.getObjectId(0);
  860. try {
  861. if (!tw.isSubtree() && (tw.getFileMode(0) != FileMode.GITLINK)) {
  862. size = tw.getObjectReader().getObjectSize(objectId, Constants.OBJ_BLOB);
  863. }
  864. } catch (Throwable t) {
  865. error(t, null, "failed to retrieve blob size for " + tw.getPathString());
  866. }
  867. return new PathModel(name, tw.getPathString(), size, tw.getFileMode(0).getBits(),
  868. objectId.getName(), commit.getName());
  869. }
  870. /**
  871. * Returns a permissions representation of the mode bits.
  872. *
  873. * @param mode
  874. * @return string representation of the mode bits
  875. */
  876. public static String getPermissionsFromMode(int mode) {
  877. if (FileMode.TREE.equals(mode)) {
  878. return "drwxr-xr-x";
  879. } else if (FileMode.REGULAR_FILE.equals(mode)) {
  880. return "-rw-r--r--";
  881. } else if (FileMode.EXECUTABLE_FILE.equals(mode)) {
  882. return "-rwxr-xr-x";
  883. } else if (FileMode.SYMLINK.equals(mode)) {
  884. return "symlink";
  885. } else if (FileMode.GITLINK.equals(mode)) {
  886. return "submodule";
  887. }
  888. return "missing";
  889. }
  890. /**
  891. * Returns a list of commits since the minimum date starting from the
  892. * specified object id.
  893. *
  894. * @param repository
  895. * @param objectId
  896. * if unspecified, HEAD is assumed.
  897. * @param minimumDate
  898. * @return list of commits
  899. */
  900. public static List<RevCommit> getRevLog(Repository repository, String objectId, Date minimumDate) {
  901. List<RevCommit> list = new ArrayList<RevCommit>();
  902. if (!hasCommits(repository)) {
  903. return list;
  904. }
  905. try {
  906. // resolve branch
  907. ObjectId branchObject;
  908. if (StringUtils.isEmpty(objectId)) {
  909. branchObject = getDefaultBranch(repository);
  910. } else {
  911. branchObject = repository.resolve(objectId);
  912. }
  913. RevWalk rw = new RevWalk(repository);
  914. rw.markStart(rw.parseCommit(branchObject));
  915. rw.setRevFilter(CommitTimeRevFilter.after(minimumDate));
  916. Iterable<RevCommit> revlog = rw;
  917. for (RevCommit rev : revlog) {
  918. list.add(rev);
  919. }
  920. rw.dispose();
  921. } catch (Throwable t) {
  922. error(t, repository, "{0} failed to get {1} revlog for minimum date {2}", objectId,
  923. minimumDate);
  924. }
  925. return list;
  926. }
  927. /**
  928. * Returns a list of commits starting from HEAD and working backwards.
  929. *
  930. * @param repository
  931. * @param maxCount
  932. * if < 0, all commits for the repository are returned.
  933. * @return list of commits
  934. */
  935. public static List<RevCommit> getRevLog(Repository repository, int maxCount) {
  936. return getRevLog(repository, null, 0, maxCount);
  937. }
  938. /**
  939. * Returns a list of commits starting from the specified objectId using an
  940. * offset and maxCount for paging. This is similar to LIMIT n OFFSET p in
  941. * SQL. If the repository does not exist or is empty, an empty list is
  942. * returned.
  943. *
  944. * @param repository
  945. * @param objectId
  946. * if unspecified, HEAD is assumed.
  947. * @param offset
  948. * @param maxCount
  949. * if < 0, all commits are returned.
  950. * @return a paged list of commits
  951. */
  952. public static List<RevCommit> getRevLog(Repository repository, String objectId, int offset,
  953. int maxCount) {
  954. return getRevLog(repository, objectId, null, offset, maxCount);
  955. }
  956. /**
  957. * Returns a list of commits for the repository or a path within the
  958. * repository. Caller may specify ending revision with objectId. Caller may
  959. * specify offset and maxCount to achieve pagination of results. If the
  960. * repository does not exist or is empty, an empty list is returned.
  961. *
  962. * @param repository
  963. * @param objectId
  964. * if unspecified, HEAD is assumed.
  965. * @param path
  966. * if unspecified, commits for repository are returned. If
  967. * specified, commits for the path are returned.
  968. * @param offset
  969. * @param maxCount
  970. * if < 0, all commits are returned.
  971. * @return a paged list of commits
  972. */
  973. public static List<RevCommit> getRevLog(Repository repository, String objectId, String path,
  974. int offset, int maxCount) {
  975. List<RevCommit> list = new ArrayList<RevCommit>();
  976. if (maxCount == 0) {
  977. return list;
  978. }
  979. if (!hasCommits(repository)) {
  980. return list;
  981. }
  982. try {
  983. // resolve branch
  984. ObjectId startRange = null;
  985. ObjectId endRange;
  986. if (StringUtils.isEmpty(objectId)) {
  987. endRange = getDefaultBranch(repository);
  988. } else {
  989. if( objectId.contains("..") ) {
  990. // range expression
  991. String[] parts = objectId.split("\\.\\.");
  992. startRange = repository.resolve(parts[0]);
  993. endRange = repository.resolve(parts[1]);
  994. } else {
  995. // objectid
  996. endRange= repository.resolve(objectId);
  997. }
  998. }
  999. if (endRange == null) {
  1000. return list;
  1001. }
  1002. RevWalk rw = new RevWalk(repository);
  1003. rw.markStart(rw.parseCommit(endRange));
  1004. if (startRange != null) {
  1005. rw.markUninteresting(rw.parseCommit(startRange));
  1006. }
  1007. if (!StringUtils.isEmpty(path)) {
  1008. TreeFilter filter = AndTreeFilter.create(
  1009. PathFilterGroup.createFromStrings(Collections.singleton(path)),
  1010. TreeFilter.ANY_DIFF);
  1011. rw.setTreeFilter(filter);
  1012. }
  1013. Iterable<RevCommit> revlog = rw;
  1014. if (offset > 0) {
  1015. int count = 0;
  1016. for (RevCommit rev : revlog) {
  1017. count++;
  1018. if (count > offset) {
  1019. list.add(rev);
  1020. if (maxCount > 0 && list.size() == maxCount) {
  1021. break;
  1022. }
  1023. }
  1024. }
  1025. } else {
  1026. for (RevCommit rev : revlog) {
  1027. list.add(rev);
  1028. if (maxCount > 0 && list.size() == maxCount) {
  1029. break;
  1030. }
  1031. }
  1032. }
  1033. rw.dispose();
  1034. } catch (Throwable t) {
  1035. error(t, repository, "{0} failed to get {1} revlog for path {2}", objectId, path);
  1036. }
  1037. return list;
  1038. }
  1039. /**
  1040. * Returns a list of commits for the repository within the range specified
  1041. * by startRangeId and endRangeId. If the repository does not exist or is
  1042. * empty, an empty list is returned.
  1043. *
  1044. * @param repository
  1045. * @param startRangeId
  1046. * the first commit (not included in results)
  1047. * @param endRangeId
  1048. * the end commit (included in results)
  1049. * @return a list of commits
  1050. */
  1051. public static List<RevCommit> getRevLog(Repository repository, String startRangeId,
  1052. String endRangeId) {
  1053. List<RevCommit> list = new ArrayList<RevCommit>();
  1054. if (!hasCommits(repository)) {
  1055. return list;
  1056. }
  1057. try {
  1058. ObjectId endRange = repository.resolve(endRangeId);
  1059. ObjectId startRange = repository.resolve(startRangeId);
  1060. RevWalk rw = new RevWalk(repository);
  1061. rw.markStart(rw.parseCommit(endRange));
  1062. if (startRange.equals(ObjectId.zeroId())) {
  1063. // maybe this is a tag or an orphan branch
  1064. list.add(rw.parseCommit(endRange));
  1065. rw.dispose();
  1066. return list;
  1067. } else {
  1068. rw.markUninteresting(rw.parseCommit(startRange));
  1069. }
  1070. Iterable<RevCommit> revlog = rw;
  1071. for (RevCommit rev : revlog) {
  1072. list.add(rev);
  1073. }
  1074. rw.dispose();
  1075. } catch (Throwable t) {
  1076. error(t, repository, "{0} failed to get revlog for {1}..{2}", startRangeId, endRangeId);
  1077. }
  1078. return list;
  1079. }
  1080. /**
  1081. * Search the commit history for a case-insensitive match to the value.
  1082. * Search results require a specified SearchType of AUTHOR, COMMITTER, or
  1083. * COMMIT. Results may be paginated using offset and maxCount. If the
  1084. * repository does not exist or is empty, an empty list is returned.
  1085. *
  1086. * @param repository
  1087. * @param objectId
  1088. * if unspecified, HEAD is assumed.
  1089. * @param value
  1090. * @param type
  1091. * AUTHOR, COMMITTER, COMMIT
  1092. * @param offset
  1093. * @param maxCount
  1094. * if < 0, all matches are returned
  1095. * @return matching list of commits
  1096. */
  1097. public static List<RevCommit> searchRevlogs(Repository repository, String objectId,
  1098. String value, final com.gitblit.Constants.SearchType type, int offset, int maxCount) {
  1099. final String lcValue = value.toLowerCase();
  1100. List<RevCommit> list = new ArrayList<RevCommit>();
  1101. if (maxCount == 0) {
  1102. return list;
  1103. }
  1104. if (!hasCommits(repository)) {
  1105. return list;
  1106. }
  1107. try {
  1108. // resolve branch
  1109. ObjectId branchObject;
  1110. if (StringUtils.isEmpty(objectId)) {
  1111. branchObject = getDefaultBranch(repository);
  1112. } else {
  1113. branchObject = repository.resolve(objectId);
  1114. }
  1115. RevWalk rw = new RevWalk(repository);
  1116. rw.setRevFilter(new RevFilter() {
  1117. @Override
  1118. public RevFilter clone() {
  1119. // FindBugs complains about this method name.
  1120. // This is part of JGit design and unrelated to Cloneable.
  1121. return this;
  1122. }
  1123. @Override
  1124. public boolean include(RevWalk walker, RevCommit commit) throws StopWalkException,
  1125. MissingObjectException, IncorrectObjectTypeException, IOException {
  1126. boolean include = false;
  1127. switch (type) {
  1128. case AUTHOR:
  1129. include = (commit.getAuthorIdent().getName().toLowerCase().indexOf(lcValue) > -1)
  1130. || (commit.getAuthorIdent().getEmailAddress().toLowerCase()
  1131. .indexOf(lcValue) > -1);
  1132. break;
  1133. case COMMITTER:
  1134. include = (commit.getCommitterIdent().getName().toLowerCase()
  1135. .indexOf(lcValue) > -1)
  1136. || (commit.getCommitterIdent().getEmailAddress().toLowerCase()
  1137. .indexOf(lcValue) > -1);
  1138. break;
  1139. case COMMIT:
  1140. include = commit.getFullMessage().toLowerCase().indexOf(lcValue) > -1;
  1141. break;
  1142. }
  1143. return include;
  1144. }
  1145. });
  1146. rw.markStart(rw.parseCommit(branchObject));
  1147. Iterable<RevCommit> revlog = rw;
  1148. if (offset > 0) {
  1149. int count = 0;
  1150. for (RevCommit rev : revlog) {
  1151. count++;
  1152. if (count > offset) {
  1153. list.add(rev);
  1154. if (maxCount > 0 && list.size() == maxCount) {
  1155. break;
  1156. }
  1157. }
  1158. }
  1159. } else {
  1160. for (RevCommit rev : revlog) {
  1161. list.add(rev);
  1162. if (maxCount > 0 && list.size() == maxCount) {
  1163. break;
  1164. }
  1165. }
  1166. }
  1167. rw.dispose();
  1168. } catch (Throwable t) {
  1169. error(t, repository, "{0} failed to {1} search revlogs for {2}", type.name(), value);
  1170. }
  1171. return list;
  1172. }
  1173. /**
  1174. * Returns the default branch to use for a repository. Normally returns
  1175. * whatever branch HEAD points to, but if HEAD points to nothing it returns
  1176. * the most recently updated branch.
  1177. *
  1178. * @param repository
  1179. * @return the objectid of a branch
  1180. * @throws Exception
  1181. */
  1182. public static ObjectId getDefaultBranch(Repository repository) throws Exception {
  1183. ObjectId object = repository.resolve(Constants.HEAD);
  1184. if (object == null) {
  1185. // no HEAD
  1186. // perhaps non-standard repository, try local branches
  1187. List<RefModel> branchModels = getLocalBranches(repository, true, -1);
  1188. if (branchModels.size() > 0) {
  1189. // use most recently updated branch
  1190. RefModel branch = null;
  1191. Date lastDate = new Date(0);
  1192. for (RefModel branchModel : branchModels) {
  1193. if (branchModel.getDate().after(lastDate)) {
  1194. branch = branchModel;
  1195. lastDate = branch.getDate();
  1196. }
  1197. }
  1198. object = branch.getReferencedObjectId();
  1199. }
  1200. }
  1201. return object;
  1202. }
  1203. /**
  1204. * Returns the target of the symbolic HEAD reference for a repository.
  1205. * Normally returns a branch reference name, but when HEAD is detached,
  1206. * the commit is matched against the known tags. The most recent matching
  1207. * tag ref name will be returned if it references the HEAD commit. If
  1208. * no match is found, the SHA1 is returned.
  1209. *
  1210. * @param repository
  1211. * @return the ref name or the SHA1 for a detached HEAD
  1212. */
  1213. public static String getHEADRef(Repository repository) {
  1214. String target = null;
  1215. try {
  1216. target = repository.getFullBranch();
  1217. if (!target.startsWith(Constants.R_HEADS)) {
  1218. // refers to an actual commit, probably a tag
  1219. // find latest tag that matches the commit, if any
  1220. List<RefModel> tagModels = getTags(repository, true, -1);
  1221. if (tagModels.size() > 0) {
  1222. RefModel tag = null;
  1223. Date lastDate = new Date(0);
  1224. for (RefModel tagModel : tagModels) {
  1225. if (tagModel.getReferencedObjectId().getName().equals(target) &&
  1226. tagModel.getDate().after(lastDate)) {
  1227. tag = tagModel;
  1228. lastDate = tag.getDate();
  1229. }
  1230. }
  1231. target = tag.getName();
  1232. }
  1233. }
  1234. } catch (Throwable t) {
  1235. error(t, repository, "{0} failed to get symbolic HEAD target");
  1236. }
  1237. return target;
  1238. }
  1239. /**
  1240. * Sets the symbolic ref HEAD to the specified target ref. The
  1241. * HEAD will be detached if the target ref is not a branch.
  1242. *
  1243. * @param repository
  1244. * @param targetRef
  1245. * @return true if successful
  1246. */
  1247. public static boolean setHEADtoRef(Repository repository, String targetRef) {
  1248. try {
  1249. // detach HEAD if target ref is not a branch
  1250. boolean detach = !targetRef.startsWith(Constants.R_HEADS);
  1251. RefUpdate.Result result;
  1252. RefUpdate head = repository.updateRef(Constants.HEAD, detach);
  1253. if (detach) { // Tag
  1254. RevCommit commit = getCommit(repository, targetRef);
  1255. head.setNewObjectId(commit.getId());
  1256. result = head.forceUpdate();
  1257. } else {
  1258. result = head.link(targetRef);
  1259. }
  1260. switch (result) {
  1261. case NEW:
  1262. case FORCED:
  1263. case NO_CHANGE:
  1264. case FAST_FORWARD:
  1265. return true;
  1266. default:
  1267. LOGGER.error(MessageFormat.format("{0} HEAD update to {1} returned result {2}",
  1268. repository.getDirectory().getAbsolutePath(), targetRef, result));
  1269. }
  1270. } catch (Throwable t) {
  1271. error(t, repository, "{0} failed to set HEAD to {1}", targetRef);
  1272. }
  1273. return false;
  1274. }
  1275. /**
  1276. * Sets the local branch ref to point to the specified commit id.
  1277. *
  1278. * @param repository
  1279. * @param branch
  1280. * @param commitId
  1281. * @return true if successful
  1282. */
  1283. public static boolean setBranchRef(Repository repository, String branch, String commitId) {
  1284. String branchName = branch;
  1285. if (!branchName.startsWith(Constants.R_HEADS)) {
  1286. branchName = Constants.R_HEADS + branch;
  1287. }
  1288. try {
  1289. RefUpdate refUpdate = repository.updateRef(branchName, false);
  1290. refUpdate.setNewObjectId(ObjectId.fromString(commitId));
  1291. RefUpdate.Result result = refUpdate.forceUpdate();
  1292. switch (result) {
  1293. case NEW:
  1294. case FORCED:
  1295. case NO_CHANGE:
  1296. case FAST_FORWARD:
  1297. return true;
  1298. default:
  1299. LOGGER.error(MessageFormat.format("{0} {1} update to {2} returned result {3}",
  1300. repository.getDirectory().getAbsolutePath(), branchName, commitId, result));
  1301. }
  1302. } catch (Throwable t) {
  1303. error(t, repository, "{0} failed to set {1} to {2}", branchName, commitId);
  1304. }
  1305. return false;
  1306. }
  1307. /**
  1308. * Deletes the specified branch ref.
  1309. *
  1310. * @param repository
  1311. * @param branch
  1312. * @return true if successful
  1313. */
  1314. public static boolean deleteBranchRef(Repository repository, String branch) {
  1315. String branchName = branch;
  1316. if (!branchName.startsWith(Constants.R_HEADS)) {
  1317. branchName = Constants.R_HEADS + branch;
  1318. }
  1319. try {
  1320. RefUpdate refUpdate = repository.updateRef(branchName, false);
  1321. refUpdate.setForceUpdate(true);
  1322. RefUpdate.Result result = refUpdate.delete();
  1323. switch (result) {
  1324. case NEW:
  1325. case FORCED:
  1326. case NO_CHANGE:
  1327. case FAST_FORWARD:
  1328. return true;
  1329. default:
  1330. LOGGER.error(MessageFormat.format("{0} failed to delete to {1} returned result {2}",
  1331. repository.getDirectory().getAbsolutePath(), branchName, result));
  1332. }
  1333. } catch (Throwable t) {
  1334. error(t, repository, "{0} failed to delete {1}", branchName);
  1335. }
  1336. return false;
  1337. }
  1338. /**
  1339. * Get the full branch and tag ref names for any potential HEAD targets.
  1340. *
  1341. * @param repository
  1342. * @return a list of ref names
  1343. */
  1344. public static List<String> getAvailableHeadTargets(Repository repository) {
  1345. List<String> targets = new ArrayList<String>();
  1346. for (RefModel branchModel : JGitUtils.getLocalBranches(repository, true, -1)) {
  1347. targets.add(branchModel.getName());
  1348. }
  1349. for (RefModel tagModel : JGitUtils.getTags(repository, true, -1)) {
  1350. targets.add(tagModel.getName());
  1351. }
  1352. return targets;
  1353. }
  1354. /**
  1355. * Returns all refs grouped by their associated object id.
  1356. *
  1357. * @param repository
  1358. * @return all refs grouped by their referenced object id
  1359. */
  1360. public static Map<ObjectId, List<RefModel>> getAllRefs(Repository repository) {
  1361. return getAllRefs(repository, true);
  1362. }
  1363. /**
  1364. * Returns all refs grouped by their associated object id.
  1365. *
  1366. * @param repository
  1367. * @param includeRemoteRefs
  1368. * @return all refs grouped by their referenced object id
  1369. */
  1370. public static Map<ObjectId, List<RefModel>> getAllRefs(Repository repository, boolean includeRemoteRefs) {
  1371. List<RefModel> list = getRefs(repository, org.eclipse.jgit.lib.RefDatabase.ALL, true, -1);
  1372. Map<ObjectId, List<RefModel>> refs = new HashMap<ObjectId, List<RefModel>>();
  1373. for (RefModel ref : list) {
  1374. if (!includeRemoteRefs && ref.getName().startsWith(Constants.R_REMOTES)) {
  1375. continue;
  1376. }
  1377. ObjectId objectid = ref.getReferencedObjectId();
  1378. if (!refs.containsKey(objectid)) {
  1379. refs.put(objectid, new ArrayList<RefModel>());
  1380. }
  1381. refs.get(objectid).add(ref);
  1382. }
  1383. return refs;
  1384. }
  1385. /**
  1386. * Returns the list of tags in the repository. If repository does not exist
  1387. * or is empty, an empty list is returned.
  1388. *
  1389. * @param repository
  1390. * @param fullName
  1391. * if true, /refs/tags/yadayadayada is returned. If false,
  1392. * yadayadayada is returned.
  1393. * @param maxCount
  1394. * if < 0, all tags are returned
  1395. * @return list of tags
  1396. */
  1397. public static List<RefModel> getTags(Repository repository, boolean fullName, int maxCount) {
  1398. return getRefs(repository, Constants.R_TAGS, fullName, maxCount);
  1399. }
  1400. /**
  1401. * Returns the list of local branches in the repository. If repository does
  1402. * not exist or is empty, an empty list is returned.
  1403. *
  1404. * @param repository
  1405. * @param fullName
  1406. * if true, /refs/heads/yadayadayada is returned. If false,
  1407. * yadayadayada is returned.
  1408. * @param maxCount
  1409. * if < 0, all local branches are returned
  1410. * @return list of local branches
  1411. */
  1412. public static List<RefModel> getLocalBranches(Repository repository, boolean fullName,
  1413. int maxCount) {
  1414. return getRefs(repository, Constants.R_HEADS, fullName, maxCount);
  1415. }
  1416. /**
  1417. * Returns the list of remote branches in the repository. If repository does
  1418. * not exist or is empty, an empty list is returned.
  1419. *
  1420. * @param repository
  1421. * @param fullName
  1422. * if true, /refs/remotes/yadayadayada is returned. If false,
  1423. * yadayadayada is returned.
  1424. * @param maxCount
  1425. * if < 0, all remote branches are returned
  1426. * @return list of remote branches
  1427. */
  1428. public static List<RefModel> getRemoteBranches(Repository repository, boolean fullName,
  1429. int maxCount) {
  1430. return getRefs(repository, Constants.R_REMOTES, fullName, maxCount);
  1431. }
  1432. /**
  1433. * Returns the list of note branches. If repository does not exist or is
  1434. * empty, an empty list is returned.
  1435. *
  1436. * @param repository
  1437. * @param fullName
  1438. * if true, /refs/notes/yadayadayada is returned. If false,
  1439. * yadayadayada is returned.
  1440. * @param maxCount
  1441. * if < 0, all note branches are returned
  1442. * @return list of note branches
  1443. */
  1444. public static List<RefModel> getNoteBranches(Repository repository, boolean fullName,
  1445. int maxCount) {
  1446. return getRefs(repository, Constants.R_NOTES, fullName, maxCount);
  1447. }
  1448. /**
  1449. * Returns the list of refs in the specified base ref. If repository does
  1450. * not exist or is empty, an empty list is returned.
  1451. *
  1452. * @param repository
  1453. * @param fullName
  1454. * if true, /refs/yadayadayada is returned. If false,
  1455. * yadayadayada is returned.
  1456. * @return list of refs
  1457. */
  1458. public static List<RefModel> getRefs(Repository repository, String baseRef) {
  1459. return getRefs(repository, baseRef, true, -1);
  1460. }
  1461. /**
  1462. * Returns a list of references in the repository matching "refs". If the
  1463. * repository is null or empty, an empty list is returned.
  1464. *
  1465. * @param repository
  1466. * @param refs
  1467. * if unspecified, all refs are returned
  1468. * @param fullName
  1469. * if true, /refs/something/yadayadayada is returned. If false,
  1470. * yadayadayada is returned.
  1471. * @param maxCount
  1472. * if < 0, all references are returned
  1473. * @return list of references
  1474. */
  1475. private static List<RefModel> getRefs(Repository repository, String refs, boolean fullName,
  1476. int maxCount) {
  1477. List<RefModel> list = new ArrayList<RefModel>();
  1478. if (maxCount == 0) {
  1479. return list;
  1480. }
  1481. if (!hasCommits(repository)) {
  1482. return list;
  1483. }
  1484. try {
  1485. Map<String, Ref> map = repository.getRefDatabase().getRefs(refs);
  1486. RevWalk rw = new RevWalk(repository);
  1487. for (Entry<String, Ref> entry : map.entrySet()) {
  1488. Ref ref = entry.getValue();
  1489. RevObject object = rw.parseAny(ref.getObjectId());
  1490. String name = entry.getKey();
  1491. if (fullName && !StringUtils.isEmpty(refs)) {
  1492. name = refs + name;
  1493. }
  1494. list.add(new RefModel(name, ref, object));
  1495. }
  1496. rw.dispose();
  1497. Collections.sort(list);
  1498. Collections.reverse(list);
  1499. if (maxCount > 0 && list.size() > maxCount) {
  1500. list = new ArrayList<RefModel>(list.subList(0, maxCount));
  1501. }
  1502. } catch (IOException e) {
  1503. error(e, repository, "{0} failed to retrieve {1}", refs);
  1504. }
  1505. return list;
  1506. }
  1507. /**
  1508. * Returns a RefModel for the gh-pages branch in the repository. If the
  1509. * branch can not be found, null is returned.
  1510. *
  1511. * @param repository
  1512. * @return a refmodel for the gh-pages branch or null
  1513. */
  1514. public static RefModel getPagesBranch(Repository repository) {
  1515. return getBranch(repository, "gh-pages");
  1516. }
  1517. /**
  1518. * Returns a RefModel for a specific branch name in the repository. If the
  1519. * branch can not be found, null is returned.
  1520. *
  1521. * @param repository
  1522. * @return a refmodel for the branch or null
  1523. */
  1524. public static RefModel getBranch(Repository repository, String name) {
  1525. RefModel branch = null;
  1526. try {
  1527. // search for the branch in local heads
  1528. for (RefModel ref : JGitUtils.getLocalBranches(repository, false, -1)) {
  1529. if (ref.reference.getName().endsWith(name)) {
  1530. branch = ref;
  1531. break;
  1532. }
  1533. }
  1534. // search for the branch in remote heads
  1535. if (branch == null) {
  1536. for (RefModel ref : JGitUtils.getRemoteBranches(repository, false, -1)) {
  1537. if (ref.reference.getName().endsWith(name)) {
  1538. branch = ref;
  1539. break;
  1540. }
  1541. }
  1542. }
  1543. } catch (Throwable t) {
  1544. LOGGER.error(MessageFormat.format("Failed to find {0} branch!", name), t);
  1545. }
  1546. return branch;
  1547. }
  1548. /**
  1549. * Returns the list of submodules for this repository.
  1550. *
  1551. * @param repository
  1552. * @param commit
  1553. * @return list of submodules
  1554. */
  1555. public static List<SubmoduleModel> getSubmodules(Repository repository, String commitId) {
  1556. RevCommit commit = getCommit(repository, commitId);
  1557. return getSubmodules(repository, commit.getTree());
  1558. }
  1559. /**
  1560. * Returns the list of submodules for this repository.
  1561. *
  1562. * @param repository
  1563. * @param commit
  1564. * @return list of submodules
  1565. */
  1566. public static List<SubmoduleModel> getSubmodules(Repository repository, RevTree tree) {
  1567. List<SubmoduleModel> list = new ArrayList<SubmoduleModel>();
  1568. byte [] blob = getByteContent(repository, tree, ".gitmodules", false);
  1569. if (blob == null) {
  1570. return list;
  1571. }
  1572. try {
  1573. BlobBasedConfig config = new BlobBasedConfig(repository.getConfig(), blob);
  1574. for (String module : config.getSubsections("submodule")) {
  1575. String path = config.getString("submodule", module, "path");
  1576. String url = config.getString("submodule", module, "url");
  1577. list.add(new SubmoduleModel(module, path, url));
  1578. }
  1579. } catch (ConfigInvalidException e) {
  1580. LOGGER.error("Failed to load .gitmodules file for " + repository.getDirectory(), e);
  1581. }
  1582. return list;
  1583. }
  1584. /**
  1585. * Returns the submodule definition for the specified path at the specified
  1586. * commit. If no module is defined for the path, null is returned.
  1587. *
  1588. * @param repository
  1589. * @param commit
  1590. * @param path
  1591. * @return a submodule definition or null if there is no submodule
  1592. */
  1593. public static SubmoduleModel getSubmoduleModel(Repository repository, String commitId, String path) {
  1594. for (SubmoduleModel model : getSubmodules(repository, commitId)) {
  1595. if (model.path.equals(path)) {
  1596. return model;
  1597. }
  1598. }
  1599. return null;
  1600. }
  1601. public static String getSubmoduleCommitId(Repository repository, String path, RevCommit commit) {
  1602. String commitId = null;
  1603. RevWalk rw = new RevWalk(repository);
  1604. TreeWalk tw = new TreeWalk(repository);
  1605. tw.setFilter(PathFilterGroup.createFromStrings(Collections.singleton(path)));
  1606. try {
  1607. tw.reset(commit.getTree());
  1608. while (tw.next()) {
  1609. if (tw.isSubtree() && !path.equals(tw.getPathString())) {
  1610. tw.enterSubtree();
  1611. continue;
  1612. }
  1613. if (FileMode.GITLINK == tw.getFileMode(0)) {
  1614. commitId = tw.getObjectId(0).getName();
  1615. break;
  1616. }
  1617. }
  1618. } catch (Throwable t) {
  1619. error(t, repository, "{0} can't find {1} in commit {2}", path, commit.name());
  1620. } finally {
  1621. rw.dispose();
  1622. tw.release();
  1623. }
  1624. return commitId;
  1625. }
  1626. /**
  1627. * Returns the list of notes entered about the commit from the refs/notes
  1628. * namespace. If the repository does not exist or is empty, an empty list is
  1629. * returned.
  1630. *
  1631. * @param repository
  1632. * @param commit
  1633. * @return list of notes
  1634. */
  1635. public static List<GitNote> getNotesOnCommit(Repository repository, RevCommit commit) {
  1636. List<GitNote> list = new ArrayList<GitNote>();
  1637. if (!hasCommits(repository)) {
  1638. return list;
  1639. }
  1640. List<RefModel> noteBranches = getNoteBranches(repository, true, -1);
  1641. for (RefModel notesRef : noteBranches) {
  1642. RevTree notesTree = JGitUtils.getCommit(repository, notesRef.getName()).getTree();
  1643. // flat notes list
  1644. String notePath = commit.getName();
  1645. String text = getStringContent(repository, notesTree, notePath);
  1646. if (!StringUtils.isEmpty(text)) {
  1647. List<RevCommit> history = getRevLog(repository, notesRef.getName(), notePath, 0, -1);
  1648. RefModel noteRef = new RefModel(notesRef.displayName, null, history.get(history
  1649. .size() - 1));
  1650. GitNote gitNote = new GitNote(noteRef, text);
  1651. list.add(gitNote);
  1652. continue;
  1653. }
  1654. // folder structure
  1655. StringBuilder sb = new StringBuilder(commit.getName());
  1656. sb.insert(2, '/');
  1657. notePath = sb.toString();
  1658. text = getStringContent(repository, notesTree, notePath);
  1659. if (!StringUtils.isEmpty(text)) {
  1660. List<RevCommit> history = getRevLog(repository, notesRef.getName(), notePath, 0, -1);
  1661. RefModel noteRef = new RefModel(notesRef.displayName, null, history.get(history
  1662. .size() - 1));
  1663. GitNote gitNote = new GitNote(noteRef, text);
  1664. list.add(gitNote);
  1665. }
  1666. }
  1667. return list;
  1668. }
  1669. /**
  1670. * this method creates an incremental revision number as a tag according to
  1671. * the amount of already existing tags, which start with a defined prefix.
  1672. *
  1673. * @param repository
  1674. * @param objectId
  1675. * @param tagger
  1676. * @param prefix
  1677. * @param intPattern
  1678. * @param message
  1679. * @return true if operation was successful, otherwise false
  1680. */
  1681. public static boolean createIncrementalRevisionTag(Repository repository,
  1682. String objectId, PersonIdent tagger, String prefix, String intPattern, String message) {
  1683. boolean result = false;
  1684. Iterator<Entry<String, Ref>> iterator = repository.getTags().entrySet().iterator();
  1685. long lastRev = 0;
  1686. while (iterator.hasNext()) {
  1687. Entry<String, Ref> entry = iterator.next();
  1688. if (entry.getKey().startsWith(prefix)) {
  1689. try {
  1690. long val = Long.parseLong(entry.getKey().substring(prefix.length()));
  1691. if (val > lastRev) {
  1692. lastRev = val;
  1693. }
  1694. } catch (Exception e) {
  1695. // this tag is NOT an incremental revision tag
  1696. }
  1697. }
  1698. }
  1699. DecimalFormat df = new DecimalFormat(intPattern);
  1700. result = createTag(repository, objectId, tagger, prefix + df.format((lastRev + 1)), message);
  1701. return result;
  1702. }
  1703. /**
  1704. * creates a tag in a repository
  1705. *
  1706. * @param repository
  1707. * @param objectId, the ref the tag points towards
  1708. * @param tagger, the person tagging the object
  1709. * @param tag, the string label
  1710. * @param message, the string message
  1711. * @return boolean, true if operation was successful, otherwise false
  1712. */
  1713. public static boolean createTag(Repository repository, String objectId, PersonIdent tagger, String tag, String message) {
  1714. try {
  1715. Git gitClient = Git.open(repository.getDirectory());
  1716. TagCommand tagCommand = gitClient.tag();
  1717. tagCommand.setTagger(tagger);
  1718. tagCommand.setMessage(message);
  1719. if (objectId != null) {
  1720. RevObject revObj = getCommit(repository, objectId);
  1721. tagCommand.setObjectId(revObj);
  1722. }
  1723. tagCommand.setName(tag);
  1724. Ref call = tagCommand.call();
  1725. return call != null ? true : false;
  1726. } catch (Exception e) {
  1727. error(e, repository, "Failed to create tag {1} in repository {0}", objectId, tag);
  1728. }
  1729. return false;
  1730. }
  1731. /**
  1732. * Create an orphaned branch in a repository.
  1733. *
  1734. * @param repository
  1735. * @param branchName
  1736. * @param author
  1737. * if unspecified, Gitblit will be the author of this new branch
  1738. * @return true if successful
  1739. */
  1740. public static boolean createOrphanBranch(Repository repository, String branchName,
  1741. PersonIdent author) {
  1742. boolean success = false;
  1743. String message = "Created branch " + branchName;
  1744. if (author == null) {
  1745. author = new PersonIdent("Gitblit", "gitblit@localhost");
  1746. }
  1747. try {
  1748. ObjectInserter odi = repository.newObjectInserter();
  1749. try {
  1750. // Create a blob object to insert into a tree
  1751. ObjectId blobId = odi.insert(Constants.OBJ_BLOB,
  1752. message.getBytes(Constants.CHARACTER_ENCODING));
  1753. // Create a tree object to reference from a commit
  1754. TreeFormatter tree = new TreeFormatter();
  1755. tree.append(".branch", FileMode.REGULAR_FILE, blobId);
  1756. ObjectId treeId = odi.insert(tree);
  1757. // Create a commit object
  1758. CommitBuilder commit = new CommitBuilder();
  1759. commit.setAuthor(author);
  1760. commit.setCommitter(author);
  1761. commit.setEncoding(Constants.CHARACTER_ENCODING);
  1762. commit.setMessage(message);
  1763. commit.setTreeId(treeId);
  1764. // Insert the commit into the repository
  1765. ObjectId commitId = odi.insert(commit);
  1766. odi.flush();
  1767. RevWalk revWalk = new RevWalk(repository);
  1768. try {
  1769. RevCommit revCommit = revWalk.parseCommit(commitId);
  1770. if (!branchName.startsWith("refs/")) {
  1771. branchName = "refs/heads/" + branchName;
  1772. }
  1773. RefUpdate ru = repository.updateRef(branchName);
  1774. ru.setNewObjectId(commitId);
  1775. ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
  1776. Result rc = ru.forceUpdate();
  1777. switch (rc) {
  1778. case NEW:
  1779. case FORCED:
  1780. case FAST_FORWARD:
  1781. success = true;
  1782. break;
  1783. default:
  1784. success = false;
  1785. }
  1786. } finally {
  1787. revWalk.release();
  1788. }
  1789. } finally {
  1790. odi.release();
  1791. }
  1792. } catch (Throwable t) {
  1793. error(t, repository, "Failed to create orphan branch {1} in repository {0}", branchName);
  1794. }
  1795. return success;
  1796. }
  1797. /**
  1798. * Reads the sparkleshare id, if present, from the repository.
  1799. *
  1800. * @param repository
  1801. * @return an id or null
  1802. */
  1803. public static String getSparkleshareId(Repository repository) {
  1804. byte[] content = getByteContent(repository, null, ".sparkleshare", false);
  1805. if (content == null) {
  1806. return null;
  1807. }
  1808. return StringUtils.decodeString(content);
  1809. }
  1810. }