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.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. assert lock.isLocked();
  128. if (mergedTables == null) {
  129. mergedTables = openMergedReftable();
  130. }
  131. return mergedTables;
  132. }
  133. /**
  134. * @return whether the given refName would be illegal in a repository that
  135. * uses loose refs.
  136. * @param refName
  137. * the name to check
  138. * @param added
  139. * a sorted set of refs we pretend have been added to the
  140. * database.
  141. * @param deleted
  142. * a set of refs we pretend have been removed from the database.
  143. * @throws IOException
  144. * on I/O problems
  145. */
  146. public boolean isNameConflicting(String refName, TreeSet<String> added,
  147. Set<String> deleted) throws IOException {
  148. lock.lock();
  149. try {
  150. Reftable table = reader();
  151. // Cannot be nested within an existing reference.
  152. int lastSlash = refName.lastIndexOf('/');
  153. while (0 < lastSlash) {
  154. String prefix = refName.substring(0, lastSlash);
  155. if (!deleted.contains(prefix)
  156. && (table.hasRef(prefix) || added.contains(prefix))) {
  157. return true;
  158. }
  159. lastSlash = refName.lastIndexOf('/', lastSlash - 1);
  160. }
  161. // Cannot be the container of an existing reference.
  162. String prefix = refName + '/';
  163. RefCursor c = table.seekRefsWithPrefix(prefix);
  164. while (c.next()) {
  165. if (!deleted.contains(c.getRef().getName())) {
  166. return true;
  167. }
  168. }
  169. String it = added.ceiling(refName + '/');
  170. if (it != null && it.startsWith(prefix)) {
  171. return true;
  172. }
  173. return false;
  174. } finally {
  175. lock.unlock();
  176. }
  177. }
  178. /**
  179. * Read a single reference.
  180. * <p>
  181. * This method expects an unshortened reference name and does not search
  182. * using the standard search path.
  183. *
  184. * @param name
  185. * the unabbreviated name of the reference.
  186. * @return the reference (if it exists); else {@code null}.
  187. * @throws java.io.IOException
  188. * the reference space cannot be accessed.
  189. */
  190. @Nullable
  191. public Ref exactRef(String name) throws IOException {
  192. lock.lock();
  193. try {
  194. Reftable table = reader();
  195. Ref ref = table.exactRef(name);
  196. if (ref != null && ref.isSymbolic()) {
  197. return table.resolve(ref);
  198. }
  199. return ref;
  200. } finally {
  201. lock.unlock();
  202. }
  203. }
  204. /**
  205. * Returns refs whose names start with a given prefix.
  206. *
  207. * @param prefix
  208. * string that names of refs should start with; may be empty (to
  209. * return all refs).
  210. * @return immutable list of refs whose names start with {@code prefix}.
  211. * @throws java.io.IOException
  212. * the reference space cannot be accessed.
  213. */
  214. public List<Ref> getRefsByPrefix(String prefix) throws IOException {
  215. List<Ref> all = new ArrayList<>();
  216. lock.lock();
  217. try {
  218. Reftable table = reader();
  219. try (RefCursor rc = RefDatabase.ALL.equals(prefix) ? table.allRefs()
  220. : table.seekRefsWithPrefix(prefix)) {
  221. while (rc.next()) {
  222. Ref ref = table.resolve(rc.getRef());
  223. if (ref != null && ref.getObjectId() != null) {
  224. all.add(ref);
  225. }
  226. }
  227. }
  228. } finally {
  229. lock.unlock();
  230. }
  231. return Collections.unmodifiableList(all);
  232. }
  233. /**
  234. * Returns all refs that resolve directly to the given {@link ObjectId}.
  235. * Includes peeled {@linkObjectId}s.
  236. *
  237. * @param id
  238. * {@link ObjectId} to resolve
  239. * @return a {@link Set} of {@link Ref}s whose tips point to the provided
  240. * id.
  241. * @throws java.io.IOException
  242. * on I/O errors.
  243. */
  244. public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
  245. lock.lock();
  246. try {
  247. RefCursor cursor = reader().byObjectId(id);
  248. Set<Ref> refs = new HashSet<>();
  249. while (cursor.next()) {
  250. refs.add(cursor.getRef());
  251. }
  252. return refs;
  253. } finally {
  254. lock.unlock();
  255. }
  256. }
  257. /**
  258. * Drops all data that might be cached in memory.
  259. */
  260. public void clearCache() {
  261. lock.lock();
  262. try {
  263. mergedTables = null;
  264. } finally {
  265. lock.unlock();
  266. }
  267. }
  268. }