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.

ReftableDatabase.java 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. package org.eclipse.jgit.internal.storage.reftable;
  2. import java.io.IOException;
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.HashSet;
  6. import java.util.List;
  7. import java.util.Set;
  8. import java.util.TreeSet;
  9. import java.util.concurrent.locks.ReentrantLock;
  10. import org.eclipse.jgit.annotations.Nullable;
  11. import org.eclipse.jgit.lib.ObjectId;
  12. import org.eclipse.jgit.lib.Ref;
  13. import org.eclipse.jgit.lib.RefDatabase;
  14. import org.eclipse.jgit.lib.ReflogReader;
  15. import org.eclipse.jgit.transport.ReceiveCommand;
  16. /**
  17. * Operations on {@link MergedReftable} that is common to various reftable-using
  18. * subclasses of {@link RefDatabase}. See
  19. * {@link org.eclipse.jgit.internal.storage.dfs.DfsReftableDatabase} for an
  20. * example.
  21. */
  22. public abstract class ReftableDatabase {
  23. // Protects mergedTables.
  24. private final ReentrantLock lock = new ReentrantLock(true);
  25. private Reftable mergedTables;
  26. /**
  27. * ReftableDatabase lazily initializes its merged reftable on the first read after
  28. * construction or clearCache() call. This function should always instantiate a new
  29. * MergedReftable based on the list of reftables specified by the underlying storage.
  30. *
  31. * @return the ReftableStack for this instance
  32. * @throws IOException
  33. * on I/O problems.
  34. */
  35. abstract protected MergedReftable openMergedReftable() throws IOException;
  36. /**
  37. * @return the next available logical timestamp for an additional reftable
  38. * in the stack.
  39. * @throws java.io.IOException
  40. * on I/O problems.
  41. */
  42. public long nextUpdateIndex() throws IOException {
  43. lock.lock();
  44. try {
  45. return reader().maxUpdateIndex() + 1;
  46. } finally {
  47. lock.unlock();
  48. }
  49. }
  50. /**
  51. * @return a ReflogReader for the given ref
  52. * @param refname
  53. * the name of the ref.
  54. * @throws IOException
  55. * on I/O problems
  56. */
  57. public ReflogReader getReflogReader(String refname) throws IOException {
  58. lock.lock();
  59. try {
  60. return new ReftableReflogReader(lock, reader(), refname);
  61. } finally {
  62. lock.unlock();
  63. }
  64. }
  65. /**
  66. * @return a ReceiveCommand for the change from oldRef to newRef
  67. * @param oldRef
  68. * a ref
  69. * @param newRef
  70. * a ref
  71. */
  72. public static ReceiveCommand toCommand(Ref oldRef, Ref newRef) {
  73. ObjectId oldId = toId(oldRef);
  74. ObjectId newId = toId(newRef);
  75. String name = oldRef != null ? oldRef.getName() : newRef.getName();
  76. if (oldRef != null && oldRef.isSymbolic()) {
  77. if (newRef != null) {
  78. if (newRef.isSymbolic()) {
  79. return ReceiveCommand.link(oldRef.getTarget().getName(),
  80. newRef.getTarget().getName(), name);
  81. }
  82. // This should pass in oldId for compat with
  83. // RefDirectoryUpdate
  84. return ReceiveCommand.unlink(oldRef.getTarget().getName(),
  85. newId, name);
  86. }
  87. return ReceiveCommand.unlink(oldRef.getTarget().getName(),
  88. ObjectId.zeroId(), name);
  89. }
  90. if (newRef != null && newRef.isSymbolic()) {
  91. if (oldRef != null) {
  92. if (oldRef.isSymbolic()) {
  93. return ReceiveCommand.link(oldRef.getTarget().getName(),
  94. newRef.getTarget().getName(), name);
  95. }
  96. return ReceiveCommand.link(oldId,
  97. newRef.getTarget().getName(), name);
  98. }
  99. return ReceiveCommand.link(ObjectId.zeroId(),
  100. newRef.getTarget().getName(), name);
  101. }
  102. return new ReceiveCommand(oldId, newId, name);
  103. }
  104. private static ObjectId toId(Ref ref) {
  105. if (ref != null) {
  106. ObjectId id = ref.getObjectId();
  107. if (id != null) {
  108. return id;
  109. }
  110. }
  111. return ObjectId.zeroId();
  112. }
  113. /**
  114. * @return the lock protecting underlying ReftableReaders against concurrent
  115. * reads.
  116. */
  117. public ReentrantLock getLock() {
  118. return lock;
  119. }
  120. /**
  121. * @return the merged reftable that is implemented by the stack of
  122. * reftables. Return value must be accessed under lock.
  123. * @throws IOException
  124. * on I/O problems
  125. */
  126. private Reftable reader() throws IOException {
  127. if (!lock.isLocked()) {
  128. throw new IllegalStateException(
  129. "must hold lock to access merged table"); //$NON-NLS-1$
  130. }
  131. if (mergedTables == null) {
  132. mergedTables = openMergedReftable();
  133. }
  134. return mergedTables;
  135. }
  136. /**
  137. * @return whether the given refName would be illegal in a repository that
  138. * uses loose refs.
  139. * @param refName
  140. * the name to check
  141. * @param added
  142. * a sorted set of refs we pretend have been added to the
  143. * database.
  144. * @param deleted
  145. * a set of refs we pretend have been removed from the database.
  146. * @throws IOException
  147. * on I/O problems
  148. */
  149. public boolean isNameConflicting(String refName, TreeSet<String> added,
  150. Set<String> deleted) throws IOException {
  151. lock.lock();
  152. try {
  153. Reftable table = reader();
  154. // Cannot be nested within an existing reference.
  155. int lastSlash = refName.lastIndexOf('/');
  156. while (0 < lastSlash) {
  157. String prefix = refName.substring(0, lastSlash);
  158. if (!deleted.contains(prefix)
  159. && (table.hasRef(prefix) || added.contains(prefix))) {
  160. return true;
  161. }
  162. lastSlash = refName.lastIndexOf('/', lastSlash - 1);
  163. }
  164. // Cannot be the container of an existing reference.
  165. String prefix = refName + '/';
  166. RefCursor c = table.seekRefsWithPrefix(prefix);
  167. while (c.next()) {
  168. if (!deleted.contains(c.getRef().getName())) {
  169. return true;
  170. }
  171. }
  172. String it = added.ceiling(refName + '/');
  173. if (it != null && it.startsWith(prefix)) {
  174. return true;
  175. }
  176. return false;
  177. } finally {
  178. lock.unlock();
  179. }
  180. }
  181. /**
  182. * Read a single reference.
  183. * <p>
  184. * This method expects an unshortened reference name and does not search
  185. * using the standard search path.
  186. *
  187. * @param name
  188. * the unabbreviated name of the reference.
  189. * @return the reference (if it exists); else {@code null}.
  190. * @throws java.io.IOException
  191. * the reference space cannot be accessed.
  192. */
  193. @Nullable
  194. public Ref exactRef(String name) throws IOException {
  195. lock.lock();
  196. try {
  197. Reftable table = reader();
  198. Ref ref = table.exactRef(name);
  199. if (ref != null && ref.isSymbolic()) {
  200. return table.resolve(ref);
  201. }
  202. return ref;
  203. } finally {
  204. lock.unlock();
  205. }
  206. }
  207. /**
  208. * Returns refs whose names start with a given prefix.
  209. *
  210. * @param prefix
  211. * string that names of refs should start with; may be empty (to
  212. * return all refs).
  213. * @return immutable list of refs whose names start with {@code prefix}.
  214. * @throws java.io.IOException
  215. * the reference space cannot be accessed.
  216. */
  217. public List<Ref> getRefsByPrefix(String prefix) throws IOException {
  218. List<Ref> all = new ArrayList<>();
  219. lock.lock();
  220. try {
  221. Reftable table = reader();
  222. try (RefCursor rc = RefDatabase.ALL.equals(prefix) ? table.allRefs()
  223. : table.seekRefsWithPrefix(prefix)) {
  224. while (rc.next()) {
  225. Ref ref = table.resolve(rc.getRef());
  226. if (ref != null && ref.getObjectId() != null) {
  227. all.add(ref);
  228. }
  229. }
  230. }
  231. } finally {
  232. lock.unlock();
  233. }
  234. return Collections.unmodifiableList(all);
  235. }
  236. /**
  237. * Returns all refs that resolve directly to the given {@link ObjectId}.
  238. * Includes peeled {@linkObjectId}s.
  239. *
  240. * @param id
  241. * {@link ObjectId} to resolve
  242. * @return a {@link Set} of {@link Ref}s whose tips point to the provided
  243. * id.
  244. * @throws java.io.IOException
  245. * on I/O errors.
  246. */
  247. public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
  248. lock.lock();
  249. try {
  250. RefCursor cursor = reader().byObjectId(id);
  251. Set<Ref> refs = new HashSet<>();
  252. while (cursor.next()) {
  253. refs.add(cursor.getRef());
  254. }
  255. return refs;
  256. } finally {
  257. lock.unlock();
  258. }
  259. }
  260. /**
  261. * Drops all data that might be cached in memory.
  262. */
  263. public void clearCache() {
  264. lock.lock();
  265. try {
  266. mergedTables = null;
  267. } finally {
  268. lock.unlock();
  269. }
  270. }
  271. }