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.

Repository.java 64KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081
  1. /*
  2. * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3. * Copyright (C) 2008-2010, Google Inc.
  4. * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * Copyright (C) 2006-2012, Shawn O. Pearce <spearce@spearce.org>
  6. * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
  7. * Copyright (C) 2017, Wim Jongman <wim.jongman@remainsoftware.com> and others
  8. *
  9. * This program and the accompanying materials are made available under the
  10. * terms of the Eclipse Distribution License v. 1.0 which is available at
  11. * https://www.eclipse.org/org/documents/edl-v10.php.
  12. *
  13. * SPDX-License-Identifier: BSD-3-Clause
  14. */
  15. package org.eclipse.jgit.lib;
  16. import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
  17. import static java.nio.charset.StandardCharsets.UTF_8;
  18. import java.io.BufferedOutputStream;
  19. import java.io.File;
  20. import java.io.FileNotFoundException;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.OutputStream;
  24. import java.io.UncheckedIOException;
  25. import java.net.URISyntaxException;
  26. import java.text.MessageFormat;
  27. import java.util.Collection;
  28. import java.util.Collections;
  29. import java.util.HashMap;
  30. import java.util.HashSet;
  31. import java.util.LinkedList;
  32. import java.util.List;
  33. import java.util.Map;
  34. import java.util.Set;
  35. import java.util.concurrent.atomic.AtomicInteger;
  36. import java.util.concurrent.atomic.AtomicLong;
  37. import java.util.regex.Pattern;
  38. import org.eclipse.jgit.annotations.NonNull;
  39. import org.eclipse.jgit.annotations.Nullable;
  40. import org.eclipse.jgit.attributes.AttributesNodeProvider;
  41. import org.eclipse.jgit.dircache.DirCache;
  42. import org.eclipse.jgit.errors.AmbiguousObjectException;
  43. import org.eclipse.jgit.errors.CorruptObjectException;
  44. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  45. import org.eclipse.jgit.errors.MissingObjectException;
  46. import org.eclipse.jgit.errors.NoWorkTreeException;
  47. import org.eclipse.jgit.errors.RevisionSyntaxException;
  48. import org.eclipse.jgit.events.IndexChangedEvent;
  49. import org.eclipse.jgit.events.IndexChangedListener;
  50. import org.eclipse.jgit.events.ListenerList;
  51. import org.eclipse.jgit.events.RepositoryEvent;
  52. import org.eclipse.jgit.internal.JGitText;
  53. import org.eclipse.jgit.revwalk.RevBlob;
  54. import org.eclipse.jgit.revwalk.RevCommit;
  55. import org.eclipse.jgit.revwalk.RevObject;
  56. import org.eclipse.jgit.revwalk.RevTree;
  57. import org.eclipse.jgit.revwalk.RevWalk;
  58. import org.eclipse.jgit.transport.RefSpec;
  59. import org.eclipse.jgit.transport.RemoteConfig;
  60. import org.eclipse.jgit.treewalk.TreeWalk;
  61. import org.eclipse.jgit.util.FS;
  62. import org.eclipse.jgit.util.FileUtils;
  63. import org.eclipse.jgit.util.IO;
  64. import org.eclipse.jgit.util.RawParseUtils;
  65. import org.eclipse.jgit.util.SystemReader;
  66. import org.slf4j.Logger;
  67. import org.slf4j.LoggerFactory;
  68. /**
  69. * Represents a Git repository.
  70. * <p>
  71. * A repository holds all objects and refs used for managing source code (could
  72. * be any type of file, but source code is what SCM's are typically used for).
  73. * <p>
  74. * The thread-safety of a {@link org.eclipse.jgit.lib.Repository} very much
  75. * depends on the concrete implementation. Applications working with a generic
  76. * {@code Repository} type must not assume the instance is thread-safe.
  77. * <ul>
  78. * <li>{@code FileRepository} is thread-safe.
  79. * <li>{@code DfsRepository} thread-safety is determined by its subclass.
  80. * </ul>
  81. */
  82. public abstract class Repository implements AutoCloseable {
  83. private static final Logger LOG = LoggerFactory.getLogger(Repository.class);
  84. private static final ListenerList globalListeners = new ListenerList();
  85. /**
  86. * Branch names containing slashes should not have a name component that is
  87. * one of the reserved device names on Windows.
  88. *
  89. * @see #normalizeBranchName(String)
  90. */
  91. private static final Pattern FORBIDDEN_BRANCH_NAME_COMPONENTS = Pattern
  92. .compile(
  93. "(^|/)(aux|com[1-9]|con|lpt[1-9]|nul|prn)(\\.[^/]*)?", //$NON-NLS-1$
  94. Pattern.CASE_INSENSITIVE);
  95. /**
  96. * Get the global listener list observing all events in this JVM.
  97. *
  98. * @return the global listener list observing all events in this JVM.
  99. */
  100. public static ListenerList getGlobalListenerList() {
  101. return globalListeners;
  102. }
  103. /** Use counter */
  104. final AtomicInteger useCnt = new AtomicInteger(1);
  105. final AtomicLong closedAt = new AtomicLong();
  106. /** Metadata directory holding the repository's critical files. */
  107. private final File gitDir;
  108. /** File abstraction used to resolve paths. */
  109. private final FS fs;
  110. private final ListenerList myListeners = new ListenerList();
  111. /** If not bare, the top level directory of the working files. */
  112. private final File workTree;
  113. /** If not bare, the index file caching the working file states. */
  114. private final File indexFile;
  115. /**
  116. * Initialize a new repository instance.
  117. *
  118. * @param options
  119. * options to configure the repository.
  120. */
  121. protected Repository(BaseRepositoryBuilder options) {
  122. gitDir = options.getGitDir();
  123. fs = options.getFS();
  124. workTree = options.getWorkTree();
  125. indexFile = options.getIndexFile();
  126. }
  127. /**
  128. * Get listeners observing only events on this repository.
  129. *
  130. * @return listeners observing only events on this repository.
  131. */
  132. @NonNull
  133. public ListenerList getListenerList() {
  134. return myListeners;
  135. }
  136. /**
  137. * Fire an event to all registered listeners.
  138. * <p>
  139. * The source repository of the event is automatically set to this
  140. * repository, before the event is delivered to any listeners.
  141. *
  142. * @param event
  143. * the event to deliver.
  144. */
  145. public void fireEvent(RepositoryEvent<?> event) {
  146. event.setRepository(this);
  147. myListeners.dispatch(event);
  148. globalListeners.dispatch(event);
  149. }
  150. /**
  151. * Create a new Git repository.
  152. * <p>
  153. * Repository with working tree is created using this method. This method is
  154. * the same as {@code create(false)}.
  155. *
  156. * @throws java.io.IOException
  157. * @see #create(boolean)
  158. */
  159. public void create() throws IOException {
  160. create(false);
  161. }
  162. /**
  163. * Create a new Git repository initializing the necessary files and
  164. * directories.
  165. *
  166. * @param bare
  167. * if true, a bare repository (a repository without a working
  168. * directory) is created.
  169. * @throws java.io.IOException
  170. * in case of IO problem
  171. */
  172. public abstract void create(boolean bare) throws IOException;
  173. /**
  174. * Get local metadata directory
  175. *
  176. * @return local metadata directory; {@code null} if repository isn't local.
  177. */
  178. /*
  179. * TODO This method should be annotated as Nullable, because in some
  180. * specific configurations metadata is not located in the local file system
  181. * (for example in memory databases). In "usual" repositories this
  182. * annotation would only cause compiler errors at places where the actual
  183. * directory can never be null.
  184. */
  185. public File getDirectory() {
  186. return gitDir;
  187. }
  188. /**
  189. * Get repository identifier.
  190. *
  191. * @return repository identifier. The returned identifier has to be unique
  192. * within a given Git server.
  193. * @since 5.4
  194. */
  195. public abstract String getIdentifier();
  196. /**
  197. * Get the object database which stores this repository's data.
  198. *
  199. * @return the object database which stores this repository's data.
  200. */
  201. @NonNull
  202. public abstract ObjectDatabase getObjectDatabase();
  203. /**
  204. * Create a new inserter to create objects in {@link #getObjectDatabase()}.
  205. *
  206. * @return a new inserter to create objects in {@link #getObjectDatabase()}.
  207. */
  208. @NonNull
  209. public ObjectInserter newObjectInserter() {
  210. return getObjectDatabase().newInserter();
  211. }
  212. /**
  213. * Create a new reader to read objects from {@link #getObjectDatabase()}.
  214. *
  215. * @return a new reader to read objects from {@link #getObjectDatabase()}.
  216. */
  217. @NonNull
  218. public ObjectReader newObjectReader() {
  219. return getObjectDatabase().newReader();
  220. }
  221. /**
  222. * Get the reference database which stores the reference namespace.
  223. *
  224. * @return the reference database which stores the reference namespace.
  225. */
  226. @NonNull
  227. public abstract RefDatabase getRefDatabase();
  228. /**
  229. * Get the configuration of this repository.
  230. *
  231. * @return the configuration of this repository.
  232. */
  233. @NonNull
  234. public abstract StoredConfig getConfig();
  235. /**
  236. * Create a new {@link org.eclipse.jgit.attributes.AttributesNodeProvider}.
  237. *
  238. * @return a new {@link org.eclipse.jgit.attributes.AttributesNodeProvider}.
  239. * This {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
  240. * is lazy loaded only once. It means that it will not be updated
  241. * after loading. Prefer creating new instance for each use.
  242. * @since 4.2
  243. */
  244. @NonNull
  245. public abstract AttributesNodeProvider createAttributesNodeProvider();
  246. /**
  247. * Get the used file system abstraction.
  248. *
  249. * @return the used file system abstraction, or {@code null} if
  250. * repository isn't local.
  251. */
  252. /*
  253. * TODO This method should be annotated as Nullable, because in some
  254. * specific configurations metadata is not located in the local file system
  255. * (for example in memory databases). In "usual" repositories this
  256. * annotation would only cause compiler errors at places where the actual
  257. * directory can never be null.
  258. */
  259. public FS getFS() {
  260. return fs;
  261. }
  262. /**
  263. * Whether the specified object is stored in this repo or any of the known
  264. * shared repositories.
  265. *
  266. * @param objectId
  267. * a {@link org.eclipse.jgit.lib.AnyObjectId} object.
  268. * @return true if the specified object is stored in this repo or any of the
  269. * known shared repositories.
  270. * @deprecated use {@code getObjectDatabase().has(objectId)}
  271. */
  272. @Deprecated
  273. public boolean hasObject(AnyObjectId objectId) {
  274. try {
  275. return getObjectDatabase().has(objectId);
  276. } catch (IOException e) {
  277. throw new UncheckedIOException(e);
  278. }
  279. }
  280. /**
  281. * Open an object from this repository.
  282. * <p>
  283. * This is a one-shot call interface which may be faster than allocating a
  284. * {@link #newObjectReader()} to perform the lookup.
  285. *
  286. * @param objectId
  287. * identity of the object to open.
  288. * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
  289. * object.
  290. * @throws org.eclipse.jgit.errors.MissingObjectException
  291. * the object does not exist.
  292. * @throws java.io.IOException
  293. * the object store cannot be accessed.
  294. */
  295. @NonNull
  296. public ObjectLoader open(AnyObjectId objectId)
  297. throws MissingObjectException, IOException {
  298. return getObjectDatabase().open(objectId);
  299. }
  300. /**
  301. * Open an object from this repository.
  302. * <p>
  303. * This is a one-shot call interface which may be faster than allocating a
  304. * {@link #newObjectReader()} to perform the lookup.
  305. *
  306. * @param objectId
  307. * identity of the object to open.
  308. * @param typeHint
  309. * hint about the type of object being requested, e.g.
  310. * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
  311. * {@link org.eclipse.jgit.lib.ObjectReader#OBJ_ANY} if the
  312. * object type is not known, or does not matter to the caller.
  313. * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
  314. * object.
  315. * @throws org.eclipse.jgit.errors.MissingObjectException
  316. * the object does not exist.
  317. * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  318. * typeHint was not OBJ_ANY, and the object's actual type does
  319. * not match typeHint.
  320. * @throws java.io.IOException
  321. * the object store cannot be accessed.
  322. */
  323. @NonNull
  324. public ObjectLoader open(AnyObjectId objectId, int typeHint)
  325. throws MissingObjectException, IncorrectObjectTypeException,
  326. IOException {
  327. return getObjectDatabase().open(objectId, typeHint);
  328. }
  329. /**
  330. * Create a command to update, create or delete a ref in this repository.
  331. *
  332. * @param ref
  333. * name of the ref the caller wants to modify.
  334. * @return an update command. The caller must finish populating this command
  335. * and then invoke one of the update methods to actually make a
  336. * change.
  337. * @throws java.io.IOException
  338. * a symbolic ref was passed in and could not be resolved back
  339. * to the base ref, as the symbolic ref could not be read.
  340. */
  341. @NonNull
  342. public RefUpdate updateRef(String ref) throws IOException {
  343. return updateRef(ref, false);
  344. }
  345. /**
  346. * Create a command to update, create or delete a ref in this repository.
  347. *
  348. * @param ref
  349. * name of the ref the caller wants to modify.
  350. * @param detach
  351. * true to create a detached head
  352. * @return an update command. The caller must finish populating this command
  353. * and then invoke one of the update methods to actually make a
  354. * change.
  355. * @throws java.io.IOException
  356. * a symbolic ref was passed in and could not be resolved back
  357. * to the base ref, as the symbolic ref could not be read.
  358. */
  359. @NonNull
  360. public RefUpdate updateRef(String ref, boolean detach) throws IOException {
  361. return getRefDatabase().newUpdate(ref, detach);
  362. }
  363. /**
  364. * Create a command to rename a ref in this repository
  365. *
  366. * @param fromRef
  367. * name of ref to rename from
  368. * @param toRef
  369. * name of ref to rename to
  370. * @return an update command that knows how to rename a branch to another.
  371. * @throws java.io.IOException
  372. * the rename could not be performed.
  373. */
  374. @NonNull
  375. public RefRename renameRef(String fromRef, String toRef) throws IOException {
  376. return getRefDatabase().newRename(fromRef, toRef);
  377. }
  378. /**
  379. * Parse a git revision string and return an object id.
  380. *
  381. * Combinations of these operators are supported:
  382. * <ul>
  383. * <li><b>HEAD</b>, <b>MERGE_HEAD</b>, <b>FETCH_HEAD</b></li>
  384. * <li><b>SHA-1</b>: a complete or abbreviated SHA-1</li>
  385. * <li><b>refs/...</b>: a complete reference name</li>
  386. * <li><b>short-name</b>: a short reference name under {@code refs/heads},
  387. * {@code refs/tags}, or {@code refs/remotes} namespace</li>
  388. * <li><b>tag-NN-gABBREV</b>: output from describe, parsed by treating
  389. * {@code ABBREV} as an abbreviated SHA-1.</li>
  390. * <li><i>id</i><b>^</b>: first parent of commit <i>id</i>, this is the same
  391. * as {@code id^1}</li>
  392. * <li><i>id</i><b>^0</b>: ensure <i>id</i> is a commit</li>
  393. * <li><i>id</i><b>^n</b>: n-th parent of commit <i>id</i></li>
  394. * <li><i>id</i><b>~n</b>: n-th historical ancestor of <i>id</i>, by first
  395. * parent. {@code id~3} is equivalent to {@code id^1^1^1} or {@code id^^^}.</li>
  396. * <li><i>id</i><b>:path</b>: Lookup path under tree named by <i>id</i></li>
  397. * <li><i>id</i><b>^{commit}</b>: ensure <i>id</i> is a commit</li>
  398. * <li><i>id</i><b>^{tree}</b>: ensure <i>id</i> is a tree</li>
  399. * <li><i>id</i><b>^{tag}</b>: ensure <i>id</i> is a tag</li>
  400. * <li><i>id</i><b>^{blob}</b>: ensure <i>id</i> is a blob</li>
  401. * </ul>
  402. *
  403. * <p>
  404. * The following operators are specified by Git conventions, but are not
  405. * supported by this method:
  406. * <ul>
  407. * <li><b>ref@{n}</b>: n-th version of ref as given by its reflog</li>
  408. * <li><b>ref@{time}</b>: value of ref at the designated time</li>
  409. * </ul>
  410. *
  411. * @param revstr
  412. * A git object references expression
  413. * @return an ObjectId or {@code null} if revstr can't be resolved to any
  414. * ObjectId
  415. * @throws org.eclipse.jgit.errors.AmbiguousObjectException
  416. * {@code revstr} contains an abbreviated ObjectId and this
  417. * repository contains more than one object which match to the
  418. * input abbreviation.
  419. * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  420. * the id parsed does not meet the type required to finish
  421. * applying the operators in the expression.
  422. * @throws org.eclipse.jgit.errors.RevisionSyntaxException
  423. * the expression is not supported by this implementation, or
  424. * does not meet the standard syntax.
  425. * @throws java.io.IOException
  426. * on serious errors
  427. */
  428. @Nullable
  429. public ObjectId resolve(String revstr)
  430. throws AmbiguousObjectException, IncorrectObjectTypeException,
  431. RevisionSyntaxException, IOException {
  432. try (RevWalk rw = new RevWalk(this)) {
  433. rw.setRetainBody(false);
  434. Object resolved = resolve(rw, revstr);
  435. if (resolved instanceof String) {
  436. final Ref ref = findRef((String) resolved);
  437. return ref != null ? ref.getLeaf().getObjectId() : null;
  438. }
  439. return (ObjectId) resolved;
  440. }
  441. }
  442. /**
  443. * Simplify an expression, but unlike {@link #resolve(String)} it will not
  444. * resolve a branch passed or resulting from the expression, such as @{-}.
  445. * Thus this method can be used to process an expression to a method that
  446. * expects a branch or revision id.
  447. *
  448. * @param revstr a {@link java.lang.String} object.
  449. * @return object id or ref name from resolved expression or {@code null} if
  450. * given expression cannot be resolved
  451. * @throws org.eclipse.jgit.errors.AmbiguousObjectException
  452. * @throws java.io.IOException
  453. */
  454. @Nullable
  455. public String simplify(String revstr)
  456. throws AmbiguousObjectException, IOException {
  457. try (RevWalk rw = new RevWalk(this)) {
  458. rw.setRetainBody(true);
  459. Object resolved = resolve(rw, revstr);
  460. if (resolved != null) {
  461. if (resolved instanceof String) {
  462. return (String) resolved;
  463. }
  464. return ((AnyObjectId) resolved).getName();
  465. }
  466. return null;
  467. }
  468. }
  469. @Nullable
  470. private Object resolve(RevWalk rw, String revstr)
  471. throws IOException {
  472. char[] revChars = revstr.toCharArray();
  473. RevObject rev = null;
  474. String name = null;
  475. int done = 0;
  476. for (int i = 0; i < revChars.length; ++i) {
  477. switch (revChars[i]) {
  478. case '^':
  479. if (rev == null) {
  480. if (name == null)
  481. if (done == 0)
  482. name = new String(revChars, done, i);
  483. else {
  484. done = i + 1;
  485. break;
  486. }
  487. rev = parseSimple(rw, name);
  488. name = null;
  489. if (rev == null)
  490. return null;
  491. }
  492. if (i + 1 < revChars.length) {
  493. switch (revChars[i + 1]) {
  494. case '0':
  495. case '1':
  496. case '2':
  497. case '3':
  498. case '4':
  499. case '5':
  500. case '6':
  501. case '7':
  502. case '8':
  503. case '9':
  504. int j;
  505. rev = rw.parseCommit(rev);
  506. for (j = i + 1; j < revChars.length; ++j) {
  507. if (!Character.isDigit(revChars[j]))
  508. break;
  509. }
  510. String parentnum = new String(revChars, i + 1, j - i
  511. - 1);
  512. int pnum;
  513. try {
  514. pnum = Integer.parseInt(parentnum);
  515. } catch (NumberFormatException e) {
  516. RevisionSyntaxException rse = new RevisionSyntaxException(
  517. JGitText.get().invalidCommitParentNumber,
  518. revstr);
  519. rse.initCause(e);
  520. throw rse;
  521. }
  522. if (pnum != 0) {
  523. RevCommit commit = (RevCommit) rev;
  524. if (pnum > commit.getParentCount())
  525. rev = null;
  526. else
  527. rev = commit.getParent(pnum - 1);
  528. }
  529. i = j - 1;
  530. done = j;
  531. break;
  532. case '{':
  533. int k;
  534. String item = null;
  535. for (k = i + 2; k < revChars.length; ++k) {
  536. if (revChars[k] == '}') {
  537. item = new String(revChars, i + 2, k - i - 2);
  538. break;
  539. }
  540. }
  541. i = k;
  542. if (item != null)
  543. if (item.equals("tree")) { //$NON-NLS-1$
  544. rev = rw.parseTree(rev);
  545. } else if (item.equals("commit")) { //$NON-NLS-1$
  546. rev = rw.parseCommit(rev);
  547. } else if (item.equals("blob")) { //$NON-NLS-1$
  548. rev = rw.peel(rev);
  549. if (!(rev instanceof RevBlob))
  550. throw new IncorrectObjectTypeException(rev,
  551. Constants.TYPE_BLOB);
  552. } else if (item.isEmpty()) {
  553. rev = rw.peel(rev);
  554. } else
  555. throw new RevisionSyntaxException(revstr);
  556. else
  557. throw new RevisionSyntaxException(revstr);
  558. done = k;
  559. break;
  560. default:
  561. rev = rw.peel(rev);
  562. if (rev instanceof RevCommit) {
  563. RevCommit commit = ((RevCommit) rev);
  564. if (commit.getParentCount() == 0)
  565. rev = null;
  566. else
  567. rev = commit.getParent(0);
  568. } else
  569. throw new IncorrectObjectTypeException(rev,
  570. Constants.TYPE_COMMIT);
  571. }
  572. } else {
  573. rev = rw.peel(rev);
  574. if (rev instanceof RevCommit) {
  575. RevCommit commit = ((RevCommit) rev);
  576. if (commit.getParentCount() == 0)
  577. rev = null;
  578. else
  579. rev = commit.getParent(0);
  580. } else
  581. throw new IncorrectObjectTypeException(rev,
  582. Constants.TYPE_COMMIT);
  583. }
  584. done = i + 1;
  585. break;
  586. case '~':
  587. if (rev == null) {
  588. if (name == null)
  589. if (done == 0)
  590. name = new String(revChars, done, i);
  591. else {
  592. done = i + 1;
  593. break;
  594. }
  595. rev = parseSimple(rw, name);
  596. name = null;
  597. if (rev == null)
  598. return null;
  599. }
  600. rev = rw.peel(rev);
  601. if (!(rev instanceof RevCommit))
  602. throw new IncorrectObjectTypeException(rev,
  603. Constants.TYPE_COMMIT);
  604. int l;
  605. for (l = i + 1; l < revChars.length; ++l) {
  606. if (!Character.isDigit(revChars[l]))
  607. break;
  608. }
  609. int dist;
  610. if (l - i > 1) {
  611. String distnum = new String(revChars, i + 1, l - i - 1);
  612. try {
  613. dist = Integer.parseInt(distnum);
  614. } catch (NumberFormatException e) {
  615. RevisionSyntaxException rse = new RevisionSyntaxException(
  616. JGitText.get().invalidAncestryLength, revstr);
  617. rse.initCause(e);
  618. throw rse;
  619. }
  620. } else
  621. dist = 1;
  622. while (dist > 0) {
  623. RevCommit commit = (RevCommit) rev;
  624. if (commit.getParentCount() == 0) {
  625. rev = null;
  626. break;
  627. }
  628. commit = commit.getParent(0);
  629. rw.parseHeaders(commit);
  630. rev = commit;
  631. --dist;
  632. }
  633. i = l - 1;
  634. done = l;
  635. break;
  636. case '@':
  637. if (rev != null)
  638. throw new RevisionSyntaxException(revstr);
  639. if (i + 1 == revChars.length)
  640. continue;
  641. if (i + 1 < revChars.length && revChars[i + 1] != '{')
  642. continue;
  643. int m;
  644. String time = null;
  645. for (m = i + 2; m < revChars.length; ++m) {
  646. if (revChars[m] == '}') {
  647. time = new String(revChars, i + 2, m - i - 2);
  648. break;
  649. }
  650. }
  651. if (time != null) {
  652. if (time.equals("upstream")) { //$NON-NLS-1$
  653. if (name == null)
  654. name = new String(revChars, done, i);
  655. if (name.isEmpty())
  656. // Currently checked out branch, HEAD if
  657. // detached
  658. name = Constants.HEAD;
  659. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  660. throw new RevisionSyntaxException(MessageFormat
  661. .format(JGitText.get().invalidRefName,
  662. name),
  663. revstr);
  664. Ref ref = findRef(name);
  665. name = null;
  666. if (ref == null)
  667. return null;
  668. if (ref.isSymbolic())
  669. ref = ref.getLeaf();
  670. name = ref.getName();
  671. RemoteConfig remoteConfig;
  672. try {
  673. remoteConfig = new RemoteConfig(getConfig(),
  674. "origin"); //$NON-NLS-1$
  675. } catch (URISyntaxException e) {
  676. RevisionSyntaxException rse = new RevisionSyntaxException(
  677. revstr);
  678. rse.initCause(e);
  679. throw rse;
  680. }
  681. String remoteBranchName = getConfig()
  682. .getString(
  683. ConfigConstants.CONFIG_BRANCH_SECTION,
  684. Repository.shortenRefName(ref.getName()),
  685. ConfigConstants.CONFIG_KEY_MERGE);
  686. List<RefSpec> fetchRefSpecs = remoteConfig
  687. .getFetchRefSpecs();
  688. for (RefSpec refSpec : fetchRefSpecs) {
  689. if (refSpec.matchSource(remoteBranchName)) {
  690. RefSpec expandFromSource = refSpec
  691. .expandFromSource(remoteBranchName);
  692. name = expandFromSource.getDestination();
  693. break;
  694. }
  695. }
  696. if (name == null)
  697. throw new RevisionSyntaxException(revstr);
  698. } else if (time.matches("^-\\d+$")) { //$NON-NLS-1$
  699. if (name != null) {
  700. throw new RevisionSyntaxException(revstr);
  701. }
  702. String previousCheckout = resolveReflogCheckout(
  703. -Integer.parseInt(time));
  704. if (ObjectId.isId(previousCheckout)) {
  705. rev = parseSimple(rw, previousCheckout);
  706. } else {
  707. name = previousCheckout;
  708. }
  709. } else {
  710. if (name == null)
  711. name = new String(revChars, done, i);
  712. if (name.isEmpty())
  713. name = Constants.HEAD;
  714. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  715. throw new RevisionSyntaxException(MessageFormat
  716. .format(JGitText.get().invalidRefName,
  717. name),
  718. revstr);
  719. Ref ref = findRef(name);
  720. name = null;
  721. if (ref == null)
  722. return null;
  723. // @{n} means current branch, not HEAD@{1} unless
  724. // detached
  725. if (ref.isSymbolic())
  726. ref = ref.getLeaf();
  727. rev = resolveReflog(rw, ref, time);
  728. }
  729. i = m;
  730. } else
  731. throw new RevisionSyntaxException(revstr);
  732. break;
  733. case ':': {
  734. RevTree tree;
  735. if (rev == null) {
  736. if (name == null)
  737. name = new String(revChars, done, i);
  738. if (name.isEmpty())
  739. name = Constants.HEAD;
  740. rev = parseSimple(rw, name);
  741. name = null;
  742. }
  743. if (rev == null)
  744. return null;
  745. tree = rw.parseTree(rev);
  746. if (i == revChars.length - 1)
  747. return tree.copy();
  748. TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(),
  749. new String(revChars, i + 1, revChars.length - i - 1),
  750. tree);
  751. return tw != null ? tw.getObjectId(0) : null;
  752. }
  753. default:
  754. if (rev != null)
  755. throw new RevisionSyntaxException(revstr);
  756. }
  757. }
  758. if (rev != null)
  759. return rev.copy();
  760. if (name != null)
  761. return name;
  762. if (done == revstr.length())
  763. return null;
  764. name = revstr.substring(done);
  765. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  766. throw new RevisionSyntaxException(
  767. MessageFormat.format(JGitText.get().invalidRefName, name),
  768. revstr);
  769. if (findRef(name) != null)
  770. return name;
  771. return resolveSimple(name);
  772. }
  773. private static boolean isHex(char c) {
  774. return ('0' <= c && c <= '9') //
  775. || ('a' <= c && c <= 'f') //
  776. || ('A' <= c && c <= 'F');
  777. }
  778. private static boolean isAllHex(String str, int ptr) {
  779. while (ptr < str.length()) {
  780. if (!isHex(str.charAt(ptr++)))
  781. return false;
  782. }
  783. return true;
  784. }
  785. @Nullable
  786. private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
  787. ObjectId id = resolveSimple(revstr);
  788. return id != null ? rw.parseAny(id) : null;
  789. }
  790. @Nullable
  791. private ObjectId resolveSimple(String revstr) throws IOException {
  792. if (ObjectId.isId(revstr))
  793. return ObjectId.fromString(revstr);
  794. if (Repository.isValidRefName("x/" + revstr)) { //$NON-NLS-1$
  795. Ref r = getRefDatabase().findRef(revstr);
  796. if (r != null)
  797. return r.getObjectId();
  798. }
  799. if (AbbreviatedObjectId.isId(revstr))
  800. return resolveAbbreviation(revstr);
  801. int dashg = revstr.indexOf("-g"); //$NON-NLS-1$
  802. if ((dashg + 5) < revstr.length() && 0 <= dashg
  803. && isHex(revstr.charAt(dashg + 2))
  804. && isHex(revstr.charAt(dashg + 3))
  805. && isAllHex(revstr, dashg + 4)) {
  806. // Possibly output from git describe?
  807. String s = revstr.substring(dashg + 2);
  808. if (AbbreviatedObjectId.isId(s))
  809. return resolveAbbreviation(s);
  810. }
  811. return null;
  812. }
  813. @Nullable
  814. private String resolveReflogCheckout(int checkoutNo)
  815. throws IOException {
  816. ReflogReader reader = getReflogReader(Constants.HEAD);
  817. if (reader == null) {
  818. return null;
  819. }
  820. List<ReflogEntry> reflogEntries = reader.getReverseEntries();
  821. for (ReflogEntry entry : reflogEntries) {
  822. CheckoutEntry checkout = entry.parseCheckout();
  823. if (checkout != null)
  824. if (checkoutNo-- == 1)
  825. return checkout.getFromBranch();
  826. }
  827. return null;
  828. }
  829. private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
  830. throws IOException {
  831. int number;
  832. try {
  833. number = Integer.parseInt(time);
  834. } catch (NumberFormatException nfe) {
  835. RevisionSyntaxException rse = new RevisionSyntaxException(
  836. MessageFormat.format(JGitText.get().invalidReflogRevision,
  837. time));
  838. rse.initCause(nfe);
  839. throw rse;
  840. }
  841. assert number >= 0;
  842. ReflogReader reader = getReflogReader(ref.getName());
  843. if (reader == null) {
  844. throw new RevisionSyntaxException(
  845. MessageFormat.format(JGitText.get().reflogEntryNotFound,
  846. Integer.valueOf(number), ref.getName()));
  847. }
  848. ReflogEntry entry = reader.getReverseEntry(number);
  849. if (entry == null)
  850. throw new RevisionSyntaxException(MessageFormat.format(
  851. JGitText.get().reflogEntryNotFound,
  852. Integer.valueOf(number), ref.getName()));
  853. return rw.parseCommit(entry.getNewId());
  854. }
  855. @Nullable
  856. private ObjectId resolveAbbreviation(String revstr) throws IOException,
  857. AmbiguousObjectException {
  858. AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
  859. try (ObjectReader reader = newObjectReader()) {
  860. Collection<ObjectId> matches = reader.resolve(id);
  861. if (matches.isEmpty())
  862. return null;
  863. else if (matches.size() == 1)
  864. return matches.iterator().next();
  865. else
  866. throw new AmbiguousObjectException(id, matches);
  867. }
  868. }
  869. /**
  870. * Increment the use counter by one, requiring a matched {@link #close()}.
  871. */
  872. public void incrementOpen() {
  873. useCnt.incrementAndGet();
  874. }
  875. /**
  876. * {@inheritDoc}
  877. * <p>
  878. * Decrement the use count, and maybe close resources.
  879. */
  880. @Override
  881. public void close() {
  882. int newCount = useCnt.decrementAndGet();
  883. if (newCount == 0) {
  884. if (RepositoryCache.isCached(this)) {
  885. closedAt.set(System.currentTimeMillis());
  886. } else {
  887. doClose();
  888. }
  889. } else if (newCount == -1) {
  890. // should not happen, only log when useCnt became negative to
  891. // minimize number of log entries
  892. String message = MessageFormat.format(JGitText.get().corruptUseCnt,
  893. toString());
  894. if (LOG.isDebugEnabled()) {
  895. LOG.debug(message, new IllegalStateException());
  896. } else {
  897. LOG.warn(message);
  898. }
  899. if (RepositoryCache.isCached(this)) {
  900. closedAt.set(System.currentTimeMillis());
  901. }
  902. }
  903. }
  904. /**
  905. * Invoked when the use count drops to zero during {@link #close()}.
  906. * <p>
  907. * The default implementation closes the object and ref databases.
  908. */
  909. protected void doClose() {
  910. getObjectDatabase().close();
  911. getRefDatabase().close();
  912. }
  913. /** {@inheritDoc} */
  914. @Override
  915. @NonNull
  916. public String toString() {
  917. String desc;
  918. File directory = getDirectory();
  919. if (directory != null)
  920. desc = directory.getPath();
  921. else
  922. desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
  923. + System.identityHashCode(this);
  924. return "Repository[" + desc + "]"; //$NON-NLS-1$ //$NON-NLS-2$
  925. }
  926. /**
  927. * Get the name of the reference that {@code HEAD} points to.
  928. * <p>
  929. * This is essentially the same as doing:
  930. *
  931. * <pre>
  932. * return exactRef(Constants.HEAD).getTarget().getName()
  933. * </pre>
  934. *
  935. * Except when HEAD is detached, in which case this method returns the
  936. * current ObjectId in hexadecimal string format.
  937. *
  938. * @return name of current branch (for example {@code refs/heads/master}),
  939. * an ObjectId in hex format if the current branch is detached, or
  940. * {@code null} if the repository is corrupt and has no HEAD
  941. * reference.
  942. * @throws java.io.IOException
  943. */
  944. @Nullable
  945. public String getFullBranch() throws IOException {
  946. Ref head = exactRef(Constants.HEAD);
  947. if (head == null) {
  948. return null;
  949. }
  950. if (head.isSymbolic()) {
  951. return head.getTarget().getName();
  952. }
  953. ObjectId objectId = head.getObjectId();
  954. if (objectId != null) {
  955. return objectId.name();
  956. }
  957. return null;
  958. }
  959. /**
  960. * Get the short name of the current branch that {@code HEAD} points to.
  961. * <p>
  962. * This is essentially the same as {@link #getFullBranch()}, except the
  963. * leading prefix {@code refs/heads/} is removed from the reference before
  964. * it is returned to the caller.
  965. *
  966. * @return name of current branch (for example {@code master}), an ObjectId
  967. * in hex format if the current branch is detached, or {@code null}
  968. * if the repository is corrupt and has no HEAD reference.
  969. * @throws java.io.IOException
  970. */
  971. @Nullable
  972. public String getBranch() throws IOException {
  973. String name = getFullBranch();
  974. if (name != null)
  975. return shortenRefName(name);
  976. return null;
  977. }
  978. /**
  979. * Objects known to exist but not expressed by {@link #getAllRefs()}.
  980. * <p>
  981. * When a repository borrows objects from another repository, it can
  982. * advertise that it safely has that other repository's references, without
  983. * exposing any other details about the other repository. This may help
  984. * a client trying to push changes avoid pushing more than it needs to.
  985. *
  986. * @return unmodifiable collection of other known objects.
  987. */
  988. @NonNull
  989. public Set<ObjectId> getAdditionalHaves() {
  990. return Collections.emptySet();
  991. }
  992. /**
  993. * Get a ref by name.
  994. *
  995. * @param name
  996. * the name of the ref to lookup. Must not be a short-hand
  997. * form; e.g., "master" is not automatically expanded to
  998. * "refs/heads/master".
  999. * @return the Ref with the given name, or {@code null} if it does not exist
  1000. * @throws java.io.IOException
  1001. * @since 4.2
  1002. */
  1003. @Nullable
  1004. public final Ref exactRef(String name) throws IOException {
  1005. return getRefDatabase().exactRef(name);
  1006. }
  1007. /**
  1008. * Search for a ref by (possibly abbreviated) name.
  1009. *
  1010. * @param name
  1011. * the name of the ref to lookup. May be a short-hand form, e.g.
  1012. * "master" which is automatically expanded to
  1013. * "refs/heads/master" if "refs/heads/master" already exists.
  1014. * @return the Ref with the given name, or {@code null} if it does not exist
  1015. * @throws java.io.IOException
  1016. * @since 4.2
  1017. */
  1018. @Nullable
  1019. public final Ref findRef(String name) throws IOException {
  1020. return getRefDatabase().findRef(name);
  1021. }
  1022. /**
  1023. * Get mutable map of all known refs, including symrefs like HEAD that may
  1024. * not point to any object yet.
  1025. *
  1026. * @return mutable map of all known refs (heads, tags, remotes).
  1027. * @deprecated use {@code getRefDatabase().getRefs()} instead.
  1028. */
  1029. @Deprecated
  1030. @NonNull
  1031. public Map<String, Ref> getAllRefs() {
  1032. try {
  1033. return getRefDatabase().getRefs(RefDatabase.ALL);
  1034. } catch (IOException e) {
  1035. throw new UncheckedIOException(e);
  1036. }
  1037. }
  1038. /**
  1039. * Get mutable map of all tags
  1040. *
  1041. * @return mutable map of all tags; key is short tag name ("v1.0") and value
  1042. * of the entry contains the ref with the full tag name
  1043. * ("refs/tags/v1.0").
  1044. * @deprecated use {@code getRefDatabase().getRefsByPrefix(R_TAGS)} instead
  1045. */
  1046. @Deprecated
  1047. @NonNull
  1048. public Map<String, Ref> getTags() {
  1049. try {
  1050. return getRefDatabase().getRefs(Constants.R_TAGS);
  1051. } catch (IOException e) {
  1052. throw new UncheckedIOException(e);
  1053. }
  1054. }
  1055. /**
  1056. * Peel a possibly unpeeled reference to an annotated tag.
  1057. * <p>
  1058. * If the ref cannot be peeled (as it does not refer to an annotated tag)
  1059. * the peeled id stays null, but {@link org.eclipse.jgit.lib.Ref#isPeeled()}
  1060. * will be true.
  1061. *
  1062. * @param ref
  1063. * The ref to peel
  1064. * @return <code>ref</code> if <code>ref.isPeeled()</code> is true; else a
  1065. * new Ref object representing the same data as Ref, but isPeeled()
  1066. * will be true and getPeeledObjectId will contain the peeled object
  1067. * (or null).
  1068. * @deprecated use {@code getRefDatabase().peel(ref)} instead.
  1069. */
  1070. @Deprecated
  1071. @NonNull
  1072. public Ref peel(Ref ref) {
  1073. try {
  1074. return getRefDatabase().peel(ref);
  1075. } catch (IOException e) {
  1076. // Historical accident; if the reference cannot be peeled due
  1077. // to some sort of repository access problem we claim that the
  1078. // same as if the reference was not an annotated tag.
  1079. return ref;
  1080. }
  1081. }
  1082. /**
  1083. * Get a map with all objects referenced by a peeled ref.
  1084. *
  1085. * @return a map with all objects referenced by a peeled ref.
  1086. */
  1087. @NonNull
  1088. public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
  1089. Map<String, Ref> allRefs = getAllRefs();
  1090. Map<AnyObjectId, Set<Ref>> ret = new HashMap<>(allRefs.size());
  1091. for (Ref ref : allRefs.values()) {
  1092. ref = peel(ref);
  1093. AnyObjectId target = ref.getPeeledObjectId();
  1094. if (target == null)
  1095. target = ref.getObjectId();
  1096. // We assume most Sets here are singletons
  1097. Set<Ref> oset = ret.put(target, Collections.singleton(ref));
  1098. if (oset != null) {
  1099. // that was not the case (rare)
  1100. if (oset.size() == 1) {
  1101. // Was a read-only singleton, we must copy to a new Set
  1102. oset = new HashSet<>(oset);
  1103. }
  1104. ret.put(target, oset);
  1105. oset.add(ref);
  1106. }
  1107. }
  1108. return ret;
  1109. }
  1110. /**
  1111. * Get the index file location or {@code null} if repository isn't local.
  1112. *
  1113. * @return the index file location or {@code null} if repository isn't
  1114. * local.
  1115. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1116. * if this is bare, which implies it has no working directory.
  1117. * See {@link #isBare()}.
  1118. */
  1119. @NonNull
  1120. public File getIndexFile() throws NoWorkTreeException {
  1121. if (isBare())
  1122. throw new NoWorkTreeException();
  1123. return indexFile;
  1124. }
  1125. /**
  1126. * Locate a reference to a commit and immediately parse its content.
  1127. * <p>
  1128. * This method only returns successfully if the commit object exists,
  1129. * is verified to be a commit, and was parsed without error.
  1130. *
  1131. * @param id
  1132. * name of the commit object.
  1133. * @return reference to the commit object. Never null.
  1134. * @throws org.eclipse.jgit.errors.MissingObjectException
  1135. * the supplied commit does not exist.
  1136. * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  1137. * the supplied id is not a commit or an annotated tag.
  1138. * @throws java.io.IOException
  1139. * a pack file or loose object could not be read.
  1140. * @since 4.8
  1141. */
  1142. public RevCommit parseCommit(AnyObjectId id) throws IncorrectObjectTypeException,
  1143. IOException, MissingObjectException {
  1144. if (id instanceof RevCommit && ((RevCommit) id).getRawBuffer() != null) {
  1145. return (RevCommit) id;
  1146. }
  1147. try (RevWalk walk = new RevWalk(this)) {
  1148. return walk.parseCommit(id);
  1149. }
  1150. }
  1151. /**
  1152. * Create a new in-core index representation and read an index from disk.
  1153. * <p>
  1154. * The new index will be read before it is returned to the caller. Read
  1155. * failures are reported as exceptions and therefore prevent the method from
  1156. * returning a partially populated index.
  1157. *
  1158. * @return a cache representing the contents of the specified index file (if
  1159. * it exists) or an empty cache if the file does not exist.
  1160. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1161. * if this is bare, which implies it has no working directory.
  1162. * See {@link #isBare()}.
  1163. * @throws java.io.IOException
  1164. * the index file is present but could not be read.
  1165. * @throws org.eclipse.jgit.errors.CorruptObjectException
  1166. * the index file is using a format or extension that this
  1167. * library does not support.
  1168. */
  1169. @NonNull
  1170. public DirCache readDirCache() throws NoWorkTreeException,
  1171. CorruptObjectException, IOException {
  1172. return DirCache.read(this);
  1173. }
  1174. /**
  1175. * Create a new in-core index representation, lock it, and read from disk.
  1176. * <p>
  1177. * The new index will be locked and then read before it is returned to the
  1178. * caller. Read failures are reported as exceptions and therefore prevent
  1179. * the method from returning a partially populated index.
  1180. *
  1181. * @return a cache representing the contents of the specified index file (if
  1182. * it exists) or an empty cache if the file does not exist.
  1183. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1184. * if this is bare, which implies it has no working directory.
  1185. * See {@link #isBare()}.
  1186. * @throws java.io.IOException
  1187. * the index file is present but could not be read, or the lock
  1188. * could not be obtained.
  1189. * @throws org.eclipse.jgit.errors.CorruptObjectException
  1190. * the index file is using a format or extension that this
  1191. * library does not support.
  1192. */
  1193. @NonNull
  1194. public DirCache lockDirCache() throws NoWorkTreeException,
  1195. CorruptObjectException, IOException {
  1196. // we want DirCache to inform us so that we can inform registered
  1197. // listeners about index changes
  1198. IndexChangedListener l = (IndexChangedEvent event) -> {
  1199. notifyIndexChanged(true);
  1200. };
  1201. return DirCache.lock(this, l);
  1202. }
  1203. /**
  1204. * Get the repository state
  1205. *
  1206. * @return the repository state
  1207. */
  1208. @NonNull
  1209. public RepositoryState getRepositoryState() {
  1210. if (isBare() || getDirectory() == null)
  1211. return RepositoryState.BARE;
  1212. // Pre Git-1.6 logic
  1213. if (new File(getWorkTree(), ".dotest").exists()) //$NON-NLS-1$
  1214. return RepositoryState.REBASING;
  1215. if (new File(getDirectory(), ".dotest-merge").exists()) //$NON-NLS-1$
  1216. return RepositoryState.REBASING_INTERACTIVE;
  1217. // From 1.6 onwards
  1218. if (new File(getDirectory(),"rebase-apply/rebasing").exists()) //$NON-NLS-1$
  1219. return RepositoryState.REBASING_REBASING;
  1220. if (new File(getDirectory(),"rebase-apply/applying").exists()) //$NON-NLS-1$
  1221. return RepositoryState.APPLY;
  1222. if (new File(getDirectory(),"rebase-apply").exists()) //$NON-NLS-1$
  1223. return RepositoryState.REBASING;
  1224. if (new File(getDirectory(),"rebase-merge/interactive").exists()) //$NON-NLS-1$
  1225. return RepositoryState.REBASING_INTERACTIVE;
  1226. if (new File(getDirectory(),"rebase-merge").exists()) //$NON-NLS-1$
  1227. return RepositoryState.REBASING_MERGE;
  1228. // Both versions
  1229. if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
  1230. // we are merging - now check whether we have unmerged paths
  1231. try {
  1232. if (!readDirCache().hasUnmergedPaths()) {
  1233. // no unmerged paths -> return the MERGING_RESOLVED state
  1234. return RepositoryState.MERGING_RESOLVED;
  1235. }
  1236. } catch (IOException e) {
  1237. throw new UncheckedIOException(e);
  1238. }
  1239. return RepositoryState.MERGING;
  1240. }
  1241. if (new File(getDirectory(), "BISECT_LOG").exists()) //$NON-NLS-1$
  1242. return RepositoryState.BISECTING;
  1243. if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
  1244. try {
  1245. if (!readDirCache().hasUnmergedPaths()) {
  1246. // no unmerged paths
  1247. return RepositoryState.CHERRY_PICKING_RESOLVED;
  1248. }
  1249. } catch (IOException e) {
  1250. throw new UncheckedIOException(e);
  1251. }
  1252. return RepositoryState.CHERRY_PICKING;
  1253. }
  1254. if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) {
  1255. try {
  1256. if (!readDirCache().hasUnmergedPaths()) {
  1257. // no unmerged paths
  1258. return RepositoryState.REVERTING_RESOLVED;
  1259. }
  1260. } catch (IOException e) {
  1261. throw new UncheckedIOException(e);
  1262. }
  1263. return RepositoryState.REVERTING;
  1264. }
  1265. return RepositoryState.SAFE;
  1266. }
  1267. /**
  1268. * Check validity of a ref name. It must not contain character that has
  1269. * a special meaning in a Git object reference expression. Some other
  1270. * dangerous characters are also excluded.
  1271. *
  1272. * For portability reasons '\' is excluded
  1273. *
  1274. * @param refName a {@link java.lang.String} object.
  1275. * @return true if refName is a valid ref name
  1276. */
  1277. public static boolean isValidRefName(String refName) {
  1278. final int len = refName.length();
  1279. if (len == 0) {
  1280. return false;
  1281. }
  1282. if (refName.endsWith(LOCK_SUFFIX)) {
  1283. return false;
  1284. }
  1285. // Refs may be stored as loose files so invalid paths
  1286. // on the local system must also be invalid refs.
  1287. try {
  1288. SystemReader.getInstance().checkPath(refName);
  1289. } catch (CorruptObjectException e) {
  1290. return false;
  1291. }
  1292. int components = 1;
  1293. char p = '\0';
  1294. for (int i = 0; i < len; i++) {
  1295. final char c = refName.charAt(i);
  1296. if (c <= ' ')
  1297. return false;
  1298. switch (c) {
  1299. case '.':
  1300. switch (p) {
  1301. case '\0': case '/': case '.':
  1302. return false;
  1303. }
  1304. if (i == len -1)
  1305. return false;
  1306. break;
  1307. case '/':
  1308. if (i == 0 || i == len - 1)
  1309. return false;
  1310. if (p == '/')
  1311. return false;
  1312. components++;
  1313. break;
  1314. case '{':
  1315. if (p == '@')
  1316. return false;
  1317. break;
  1318. case '~': case '^': case ':':
  1319. case '?': case '[': case '*':
  1320. case '\\':
  1321. case '\u007F':
  1322. return false;
  1323. }
  1324. p = c;
  1325. }
  1326. return components > 1;
  1327. }
  1328. /**
  1329. * Normalizes the passed branch name into a possible valid branch name. The
  1330. * validity of the returned name should be checked by a subsequent call to
  1331. * {@link #isValidRefName(String)}.
  1332. * <p>
  1333. * Future implementations of this method could be more restrictive or more
  1334. * lenient about the validity of specific characters in the returned name.
  1335. * <p>
  1336. * The current implementation returns the trimmed input string if this is
  1337. * already a valid branch name. Otherwise it returns a trimmed string with
  1338. * special characters not allowed by {@link #isValidRefName(String)}
  1339. * replaced by hyphens ('-') and blanks replaced by underscores ('_').
  1340. * Leading and trailing slashes, dots, hyphens, and underscores are removed.
  1341. *
  1342. * @param name
  1343. * to normalize
  1344. * @return The normalized name or an empty String if it is {@code null} or
  1345. * empty.
  1346. * @since 4.7
  1347. * @see #isValidRefName(String)
  1348. */
  1349. public static String normalizeBranchName(String name) {
  1350. if (name == null || name.isEmpty()) {
  1351. return ""; //$NON-NLS-1$
  1352. }
  1353. String result = name.trim();
  1354. String fullName = result.startsWith(Constants.R_HEADS) ? result
  1355. : Constants.R_HEADS + result;
  1356. if (isValidRefName(fullName)) {
  1357. return result;
  1358. }
  1359. // All Unicode blanks to underscore
  1360. result = result.replaceAll("(?:\\h|\\v)+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
  1361. StringBuilder b = new StringBuilder();
  1362. char p = '/';
  1363. for (int i = 0, len = result.length(); i < len; i++) {
  1364. char c = result.charAt(i);
  1365. if (c < ' ' || c == 127) {
  1366. continue;
  1367. }
  1368. // Substitute a dash for problematic characters
  1369. switch (c) {
  1370. case '\\':
  1371. case '^':
  1372. case '~':
  1373. case ':':
  1374. case '?':
  1375. case '*':
  1376. case '[':
  1377. case '@':
  1378. case '<':
  1379. case '>':
  1380. case '|':
  1381. case '"':
  1382. c = '-';
  1383. break;
  1384. default:
  1385. break;
  1386. }
  1387. // Collapse multiple slashes, dashes, dots, underscores, and omit
  1388. // dashes, dots, and underscores following a slash.
  1389. switch (c) {
  1390. case '/':
  1391. if (p == '/') {
  1392. continue;
  1393. }
  1394. p = '/';
  1395. break;
  1396. case '.':
  1397. case '_':
  1398. case '-':
  1399. if (p == '/' || p == '-') {
  1400. continue;
  1401. }
  1402. p = '-';
  1403. break;
  1404. default:
  1405. p = c;
  1406. break;
  1407. }
  1408. b.append(c);
  1409. }
  1410. // Strip trailing special characters, and avoid the .lock extension
  1411. result = b.toString().replaceFirst("[/_.-]+$", "") //$NON-NLS-1$ //$NON-NLS-2$
  1412. .replaceAll("\\.lock($|/)", "_lock$1"); //$NON-NLS-1$ //$NON-NLS-2$
  1413. return FORBIDDEN_BRANCH_NAME_COMPONENTS.matcher(result)
  1414. .replaceAll("$1+$2$3"); //$NON-NLS-1$
  1415. }
  1416. /**
  1417. * Strip work dir and return normalized repository path.
  1418. *
  1419. * @param workDir
  1420. * Work dir
  1421. * @param file
  1422. * File whose path shall be stripped of its workdir
  1423. * @return normalized repository relative path or the empty string if the
  1424. * file is not relative to the work directory.
  1425. */
  1426. @NonNull
  1427. public static String stripWorkDir(File workDir, File file) {
  1428. final String filePath = file.getPath();
  1429. final String workDirPath = workDir.getPath();
  1430. if (filePath.length() <= workDirPath.length()
  1431. || filePath.charAt(workDirPath.length()) != File.separatorChar
  1432. || !filePath.startsWith(workDirPath)) {
  1433. File absWd = workDir.isAbsolute() ? workDir
  1434. : workDir.getAbsoluteFile();
  1435. File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
  1436. if (absWd.equals(workDir) && absFile.equals(file)) {
  1437. return ""; //$NON-NLS-1$
  1438. }
  1439. return stripWorkDir(absWd, absFile);
  1440. }
  1441. String relName = filePath.substring(workDirPath.length() + 1);
  1442. if (File.separatorChar != '/') {
  1443. relName = relName.replace(File.separatorChar, '/');
  1444. }
  1445. return relName;
  1446. }
  1447. /**
  1448. * Whether this repository is bare
  1449. *
  1450. * @return true if this is bare, which implies it has no working directory.
  1451. */
  1452. public boolean isBare() {
  1453. return workTree == null;
  1454. }
  1455. /**
  1456. * Get the root directory of the working tree, where files are checked out
  1457. * for viewing and editing.
  1458. *
  1459. * @return the root directory of the working tree, where files are checked
  1460. * out for viewing and editing.
  1461. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1462. * if this is bare, which implies it has no working directory.
  1463. * See {@link #isBare()}.
  1464. */
  1465. @NonNull
  1466. public File getWorkTree() throws NoWorkTreeException {
  1467. if (isBare())
  1468. throw new NoWorkTreeException();
  1469. return workTree;
  1470. }
  1471. /**
  1472. * Force a scan for changed refs. Fires an IndexChangedEvent(false) if
  1473. * changes are detected.
  1474. *
  1475. * @throws java.io.IOException
  1476. */
  1477. public abstract void scanForRepoChanges() throws IOException;
  1478. /**
  1479. * Notify that the index changed by firing an IndexChangedEvent.
  1480. *
  1481. * @param internal
  1482. * {@code true} if the index was changed by the same
  1483. * JGit process
  1484. * @since 5.0
  1485. */
  1486. public abstract void notifyIndexChanged(boolean internal);
  1487. /**
  1488. * Get a shortened more user friendly ref name
  1489. *
  1490. * @param refName
  1491. * a {@link java.lang.String} object.
  1492. * @return a more user friendly ref name
  1493. */
  1494. @NonNull
  1495. public static String shortenRefName(String refName) {
  1496. if (refName.startsWith(Constants.R_HEADS))
  1497. return refName.substring(Constants.R_HEADS.length());
  1498. if (refName.startsWith(Constants.R_TAGS))
  1499. return refName.substring(Constants.R_TAGS.length());
  1500. if (refName.startsWith(Constants.R_REMOTES))
  1501. return refName.substring(Constants.R_REMOTES.length());
  1502. return refName;
  1503. }
  1504. /**
  1505. * Get a shortened more user friendly remote tracking branch name
  1506. *
  1507. * @param refName
  1508. * a {@link java.lang.String} object.
  1509. * @return the remote branch name part of <code>refName</code>, i.e. without
  1510. * the <code>refs/remotes/&lt;remote&gt;</code> prefix, if
  1511. * <code>refName</code> represents a remote tracking branch;
  1512. * otherwise {@code null}.
  1513. * @since 3.4
  1514. */
  1515. @Nullable
  1516. public String shortenRemoteBranchName(String refName) {
  1517. for (String remote : getRemoteNames()) {
  1518. String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
  1519. if (refName.startsWith(remotePrefix))
  1520. return refName.substring(remotePrefix.length());
  1521. }
  1522. return null;
  1523. }
  1524. /**
  1525. * Get remote name
  1526. *
  1527. * @param refName
  1528. * a {@link java.lang.String} object.
  1529. * @return the remote name part of <code>refName</code>, i.e. without the
  1530. * <code>refs/remotes/&lt;remote&gt;</code> prefix, if
  1531. * <code>refName</code> represents a remote tracking branch;
  1532. * otherwise {@code null}.
  1533. * @since 3.4
  1534. */
  1535. @Nullable
  1536. public String getRemoteName(String refName) {
  1537. for (String remote : getRemoteNames()) {
  1538. String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
  1539. if (refName.startsWith(remotePrefix))
  1540. return remote;
  1541. }
  1542. return null;
  1543. }
  1544. /**
  1545. * Read the {@code GIT_DIR/description} file for gitweb.
  1546. *
  1547. * @return description text; null if no description has been configured.
  1548. * @throws java.io.IOException
  1549. * description cannot be accessed.
  1550. * @since 4.6
  1551. */
  1552. @Nullable
  1553. public String getGitwebDescription() throws IOException {
  1554. return null;
  1555. }
  1556. /**
  1557. * Set the {@code GIT_DIR/description} file for gitweb.
  1558. *
  1559. * @param description
  1560. * new description; null to clear the description.
  1561. * @throws java.io.IOException
  1562. * description cannot be persisted.
  1563. * @since 4.6
  1564. */
  1565. public void setGitwebDescription(@Nullable String description)
  1566. throws IOException {
  1567. throw new IOException(JGitText.get().unsupportedRepositoryDescription);
  1568. }
  1569. /**
  1570. * Get the reflog reader
  1571. *
  1572. * @param refName
  1573. * a {@link java.lang.String} object.
  1574. * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied
  1575. * refname, or {@code null} if the named ref does not exist.
  1576. * @throws java.io.IOException
  1577. * the ref could not be accessed.
  1578. * @since 3.0
  1579. */
  1580. @Nullable
  1581. public abstract ReflogReader getReflogReader(String refName)
  1582. throws IOException;
  1583. /**
  1584. * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
  1585. * file operations triggering a merge will store a template for the commit
  1586. * message of the merge commit.
  1587. *
  1588. * @return a String containing the content of the MERGE_MSG file or
  1589. * {@code null} if this file doesn't exist
  1590. * @throws java.io.IOException
  1591. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1592. * if this is bare, which implies it has no working directory.
  1593. * See {@link #isBare()}.
  1594. */
  1595. @Nullable
  1596. public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
  1597. return readCommitMsgFile(Constants.MERGE_MSG);
  1598. }
  1599. /**
  1600. * Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
  1601. * triggering a merge will store a template for the commit message of the
  1602. * merge commit. If <code>null</code> is specified as message the file will
  1603. * be deleted.
  1604. *
  1605. * @param msg
  1606. * the message which should be written or <code>null</code> to
  1607. * delete the file
  1608. * @throws java.io.IOException
  1609. */
  1610. public void writeMergeCommitMsg(String msg) throws IOException {
  1611. File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
  1612. writeCommitMsg(mergeMsgFile, msg);
  1613. }
  1614. /**
  1615. * Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In
  1616. * this file hooks triggered by an operation may read or modify the current
  1617. * commit message.
  1618. *
  1619. * @return a String containing the content of the COMMIT_EDITMSG file or
  1620. * {@code null} if this file doesn't exist
  1621. * @throws java.io.IOException
  1622. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1623. * if this is bare, which implies it has no working directory.
  1624. * See {@link #isBare()}.
  1625. * @since 4.0
  1626. */
  1627. @Nullable
  1628. public String readCommitEditMsg() throws IOException, NoWorkTreeException {
  1629. return readCommitMsgFile(Constants.COMMIT_EDITMSG);
  1630. }
  1631. /**
  1632. * Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks
  1633. * triggered by an operation may read or modify the current commit message.
  1634. * If {@code null} is specified as message the file will be deleted.
  1635. *
  1636. * @param msg
  1637. * the message which should be written or {@code null} to delete
  1638. * the file
  1639. * @throws java.io.IOException
  1640. * @since 4.0
  1641. */
  1642. public void writeCommitEditMsg(String msg) throws IOException {
  1643. File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
  1644. writeCommitMsg(commiEditMsgFile, msg);
  1645. }
  1646. /**
  1647. * Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
  1648. * file operations triggering a merge will store the IDs of all heads which
  1649. * should be merged together with HEAD.
  1650. *
  1651. * @return a list of commits which IDs are listed in the MERGE_HEAD file or
  1652. * {@code null} if this file doesn't exist. Also if the file exists
  1653. * but is empty {@code null} will be returned
  1654. * @throws java.io.IOException
  1655. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1656. * if this is bare, which implies it has no working directory.
  1657. * See {@link #isBare()}.
  1658. */
  1659. @Nullable
  1660. public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
  1661. if (isBare() || getDirectory() == null)
  1662. throw new NoWorkTreeException();
  1663. byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
  1664. if (raw == null)
  1665. return null;
  1666. LinkedList<ObjectId> heads = new LinkedList<>();
  1667. for (int p = 0; p < raw.length;) {
  1668. heads.add(ObjectId.fromString(raw, p));
  1669. p = RawParseUtils
  1670. .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
  1671. }
  1672. return heads;
  1673. }
  1674. /**
  1675. * Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations
  1676. * triggering a merge will store the IDs of all heads which should be merged
  1677. * together with HEAD. If <code>null</code> is specified as list of commits
  1678. * the file will be deleted
  1679. *
  1680. * @param heads
  1681. * a list of commits which IDs should be written to
  1682. * $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file
  1683. * @throws java.io.IOException
  1684. */
  1685. public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException {
  1686. writeHeadsFile(heads, Constants.MERGE_HEAD);
  1687. }
  1688. /**
  1689. * Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD.
  1690. *
  1691. * @return object id from CHERRY_PICK_HEAD file or {@code null} if this file
  1692. * doesn't exist. Also if the file exists but is empty {@code null}
  1693. * will be returned
  1694. * @throws java.io.IOException
  1695. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1696. * if this is bare, which implies it has no working directory.
  1697. * See {@link #isBare()}.
  1698. */
  1699. @Nullable
  1700. public ObjectId readCherryPickHead() throws IOException,
  1701. NoWorkTreeException {
  1702. if (isBare() || getDirectory() == null)
  1703. throw new NoWorkTreeException();
  1704. byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
  1705. if (raw == null)
  1706. return null;
  1707. return ObjectId.fromString(raw, 0);
  1708. }
  1709. /**
  1710. * Return the information stored in the file $GIT_DIR/REVERT_HEAD.
  1711. *
  1712. * @return object id from REVERT_HEAD file or {@code null} if this file
  1713. * doesn't exist. Also if the file exists but is empty {@code null}
  1714. * will be returned
  1715. * @throws java.io.IOException
  1716. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1717. * if this is bare, which implies it has no working directory.
  1718. * See {@link #isBare()}.
  1719. */
  1720. @Nullable
  1721. public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
  1722. if (isBare() || getDirectory() == null)
  1723. throw new NoWorkTreeException();
  1724. byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
  1725. if (raw == null)
  1726. return null;
  1727. return ObjectId.fromString(raw, 0);
  1728. }
  1729. /**
  1730. * Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in
  1731. * case of conflicts to store the cherry which was tried to be picked.
  1732. *
  1733. * @param head
  1734. * an object id of the cherry commit or <code>null</code> to
  1735. * delete the file
  1736. * @throws java.io.IOException
  1737. */
  1738. public void writeCherryPickHead(ObjectId head) throws IOException {
  1739. List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
  1740. : null;
  1741. writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD);
  1742. }
  1743. /**
  1744. * Write revert commit into $GIT_DIR/REVERT_HEAD. This is used in case of
  1745. * conflicts to store the revert which was tried to be picked.
  1746. *
  1747. * @param head
  1748. * an object id of the revert commit or <code>null</code> to
  1749. * delete the file
  1750. * @throws java.io.IOException
  1751. */
  1752. public void writeRevertHead(ObjectId head) throws IOException {
  1753. List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
  1754. : null;
  1755. writeHeadsFile(heads, Constants.REVERT_HEAD);
  1756. }
  1757. /**
  1758. * Write original HEAD commit into $GIT_DIR/ORIG_HEAD.
  1759. *
  1760. * @param head
  1761. * an object id of the original HEAD commit or <code>null</code>
  1762. * to delete the file
  1763. * @throws java.io.IOException
  1764. */
  1765. public void writeOrigHead(ObjectId head) throws IOException {
  1766. List<ObjectId> heads = head != null ? Collections.singletonList(head)
  1767. : null;
  1768. writeHeadsFile(heads, Constants.ORIG_HEAD);
  1769. }
  1770. /**
  1771. * Return the information stored in the file $GIT_DIR/ORIG_HEAD.
  1772. *
  1773. * @return object id from ORIG_HEAD file or {@code null} if this file
  1774. * doesn't exist. Also if the file exists but is empty {@code null}
  1775. * will be returned
  1776. * @throws java.io.IOException
  1777. * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1778. * if this is bare, which implies it has no working directory.
  1779. * See {@link #isBare()}.
  1780. */
  1781. @Nullable
  1782. public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
  1783. if (isBare() || getDirectory() == null)
  1784. throw new NoWorkTreeException();
  1785. byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
  1786. return raw != null ? ObjectId.fromString(raw, 0) : null;
  1787. }
  1788. /**
  1789. * Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this
  1790. * file operations triggering a squashed merge will store a template for the
  1791. * commit message of the squash commit.
  1792. *
  1793. * @return a String containing the content of the SQUASH_MSG file or
  1794. * {@code null} if this file doesn't exist
  1795. * @throws java.io.IOException
  1796. * @throws NoWorkTreeException
  1797. * if this is bare, which implies it has no working directory.
  1798. * See {@link #isBare()}.
  1799. */
  1800. @Nullable
  1801. public String readSquashCommitMsg() throws IOException {
  1802. return readCommitMsgFile(Constants.SQUASH_MSG);
  1803. }
  1804. /**
  1805. * Write new content to the file $GIT_DIR/SQUASH_MSG. In this file
  1806. * operations triggering a squashed merge will store a template for the
  1807. * commit message of the squash commit. If <code>null</code> is specified as
  1808. * message the file will be deleted.
  1809. *
  1810. * @param msg
  1811. * the message which should be written or <code>null</code> to
  1812. * delete the file
  1813. * @throws java.io.IOException
  1814. */
  1815. public void writeSquashCommitMsg(String msg) throws IOException {
  1816. File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
  1817. writeCommitMsg(squashMsgFile, msg);
  1818. }
  1819. @Nullable
  1820. private String readCommitMsgFile(String msgFilename) throws IOException {
  1821. if (isBare() || getDirectory() == null)
  1822. throw new NoWorkTreeException();
  1823. File mergeMsgFile = new File(getDirectory(), msgFilename);
  1824. try {
  1825. return RawParseUtils.decode(IO.readFully(mergeMsgFile));
  1826. } catch (FileNotFoundException e) {
  1827. if (mergeMsgFile.exists()) {
  1828. throw e;
  1829. }
  1830. // the file has disappeared in the meantime ignore it
  1831. return null;
  1832. }
  1833. }
  1834. private void writeCommitMsg(File msgFile, String msg) throws IOException {
  1835. if (msg != null) {
  1836. try (FileOutputStream fos = new FileOutputStream(msgFile)) {
  1837. fos.write(msg.getBytes(UTF_8));
  1838. }
  1839. } else {
  1840. FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
  1841. }
  1842. }
  1843. /**
  1844. * Read a file from the git directory.
  1845. *
  1846. * @param filename
  1847. * @return the raw contents or {@code null} if the file doesn't exist or is
  1848. * empty
  1849. * @throws IOException
  1850. */
  1851. private byte[] readGitDirectoryFile(String filename) throws IOException {
  1852. File file = new File(getDirectory(), filename);
  1853. try {
  1854. byte[] raw = IO.readFully(file);
  1855. return raw.length > 0 ? raw : null;
  1856. } catch (FileNotFoundException notFound) {
  1857. if (file.exists()) {
  1858. throw notFound;
  1859. }
  1860. return null;
  1861. }
  1862. }
  1863. /**
  1864. * Write the given heads to a file in the git directory.
  1865. *
  1866. * @param heads
  1867. * a list of object ids to write or null if the file should be
  1868. * deleted.
  1869. * @param filename
  1870. * @throws FileNotFoundException
  1871. * @throws IOException
  1872. */
  1873. private void writeHeadsFile(List<? extends ObjectId> heads, String filename)
  1874. throws FileNotFoundException, IOException {
  1875. File headsFile = new File(getDirectory(), filename);
  1876. if (heads != null) {
  1877. try (OutputStream bos = new BufferedOutputStream(
  1878. new FileOutputStream(headsFile))) {
  1879. for (ObjectId id : heads) {
  1880. id.copyTo(bos);
  1881. bos.write('\n');
  1882. }
  1883. }
  1884. } else {
  1885. FileUtils.delete(headsFile, FileUtils.SKIP_MISSING);
  1886. }
  1887. }
  1888. /**
  1889. * Read a file formatted like the git-rebase-todo file. The "done" file is
  1890. * also formatted like the git-rebase-todo file. These files can be found in
  1891. * .git/rebase-merge/ or .git/rebase-append/ folders.
  1892. *
  1893. * @param path
  1894. * path to the file relative to the repository's git-dir. E.g.
  1895. * "rebase-merge/git-rebase-todo" or "rebase-append/done"
  1896. * @param includeComments
  1897. * <code>true</code> if also comments should be reported
  1898. * @return the list of steps
  1899. * @throws java.io.IOException
  1900. * @since 3.2
  1901. */
  1902. @NonNull
  1903. public List<RebaseTodoLine> readRebaseTodo(String path,
  1904. boolean includeComments)
  1905. throws IOException {
  1906. return new RebaseTodoFile(this).readRebaseTodo(path, includeComments);
  1907. }
  1908. /**
  1909. * Write a file formatted like a git-rebase-todo file.
  1910. *
  1911. * @param path
  1912. * path to the file relative to the repository's git-dir. E.g.
  1913. * "rebase-merge/git-rebase-todo" or "rebase-append/done"
  1914. * @param steps
  1915. * the steps to be written
  1916. * @param append
  1917. * whether to append to an existing file or to write a new file
  1918. * @throws java.io.IOException
  1919. * @since 3.2
  1920. */
  1921. public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps,
  1922. boolean append)
  1923. throws IOException {
  1924. new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append);
  1925. }
  1926. /**
  1927. * Get the names of all known remotes
  1928. *
  1929. * @return the names of all known remotes
  1930. * @since 3.4
  1931. */
  1932. @NonNull
  1933. public Set<String> getRemoteNames() {
  1934. return getConfig()
  1935. .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
  1936. }
  1937. /**
  1938. * Check whether any housekeeping is required; if yes, run garbage
  1939. * collection; if not, exit without performing any work. Some JGit commands
  1940. * run autoGC after performing operations that could create many loose
  1941. * objects.
  1942. * <p>
  1943. * Currently this option is supported for repositories of type
  1944. * {@code FileRepository} only. See
  1945. * {@link org.eclipse.jgit.internal.storage.file.GC#setAuto(boolean)} for
  1946. * configuration details.
  1947. *
  1948. * @param monitor
  1949. * to report progress
  1950. * @since 4.6
  1951. */
  1952. public void autoGC(ProgressMonitor monitor) {
  1953. // default does nothing
  1954. }
  1955. }