@Override public boolean isNameConflicting(String name) throws IOException { RefList<Ref> packed = getPackedRefs(); RefList<LooseRef> loose = getLooseRefs(); // Cannot be nested within an existing reference. int lastSlash = name.lastIndexOf('/'); while (0 < lastSlash) { String needle = name.substring(0, lastSlash); if (loose.contains(needle) || packed.contains(needle)) return true; lastSlash = name.lastIndexOf('/', lastSlash - 1); } // Cannot be the container of an existing reference. String prefix = name + '/'; int idx; idx = -(packed.find(prefix) + 1); if (idx < packed.size() && packed.get(idx).getName().startsWith(prefix)) return true; idx = -(loose.find(prefix) + 1); if (idx < loose.size() && loose.get(idx).getName().startsWith(prefix)) return true; return false; }
private Ref resolve( final Ref ref, int depth, String prefix, RefList<LooseRef> loose, RefList<Ref> packed) throws IOException { if (ref.isSymbolic()) { Ref dst = ref.getTarget(); if (MAX_SYMBOLIC_REF_DEPTH <= depth) return null; // claim it doesn't exist // If the cached value can be assumed to be current due to a // recent scan of the loose directory, use it. if (loose != null && dst.getName().startsWith(prefix)) { int idx; if (0 <= (idx = loose.find(dst.getName()))) dst = loose.get(idx); else if (0 <= (idx = packed.find(dst.getName()))) dst = packed.get(idx); else return ref; } else { dst = readRef(dst.getName(), packed); if (dst == null) return ref; } dst = resolve(dst, depth + 1, prefix, loose, packed); if (dst == null) return null; return new SymbolicRef(ref.getName(), dst); } return ref; }
/** * Adds a set of refs to the set of packed-refs. Only non-symbolic refs are added. If a ref with * the given name already existed in packed-refs it is updated with the new value. Each loose ref * which was added to the packed-ref file is deleted. If a given ref can't be locked it will not * be added to the pack file. * * @param refs the refs to be added. Must be fully qualified. * @throws IOException */ public void pack(List<String> refs) throws IOException { if (refs.size() == 0) return; FS fs = parent.getFS(); // Lock the packed refs file and read the content LockFile lck = new LockFile(packedRefsFile); if (!lck.lock()) throw new IOException(MessageFormat.format(JGitText.get().cannotLock, packedRefsFile)); try { final PackedRefList packed = getPackedRefs(); RefList<Ref> cur = readPackedRefs(); // Iterate over all refs to be packed for (String refName : refs) { Ref ref = readRef(refName, cur); if (ref.isSymbolic()) continue; // can't pack symbolic refs // Add/Update it to packed-refs int idx = cur.find(refName); if (idx >= 0) cur = cur.set(idx, peeledPackedRef(ref)); else cur = cur.add(idx, peeledPackedRef(ref)); } // The new content for packed-refs is collected. Persist it. commitPackedRefs(lck, cur, packed); // Now delete the loose refs which are now packed for (String refName : refs) { // Lock the loose ref File refFile = fileFor(refName); if (!fs.exists(refFile)) continue; LockFile rLck = new LockFile(refFile); if (!rLck.lock()) continue; try { LooseRef currentLooseRef = scanRef(null, refName); if (currentLooseRef == null || currentLooseRef.isSymbolic()) continue; Ref packedRef = cur.get(refName); ObjectId clr_oid = currentLooseRef.getObjectId(); if (clr_oid != null && clr_oid.equals(packedRef.getObjectId())) { RefList<LooseRef> curLoose, newLoose; do { curLoose = looseRefs.get(); int idx = curLoose.find(refName); if (idx < 0) break; newLoose = curLoose.remove(idx); } while (!looseRefs.compareAndSet(curLoose, newLoose)); int levels = levelsIn(refName) - 2; delete(refFile, levels, rLck); } } finally { rLck.unlock(); } } // Don't fire refsChanged. The refs have not change, only their // storage. } finally { lck.unlock(); } }
/** * Obtain a modified copy of the cache with the ref removed. * * <p>This cache instance is not modified by this method. * * @param refName reference to remove, if it exists. * @return a copy of this cache, with the reference removed. */ public RefCache remove(String refName) { RefList<Ref> newIds = this.ids; int p = newIds.find(refName); if (0 <= p) newIds = newIds.remove(p); RefList<Ref> newSym = this.sym; p = newSym.find(refName); if (0 <= p) newSym = newSym.remove(p); return new RefCache(newIds, newSym); }
private Ref readRef(String name, RefList<Ref> packed) throws IOException { final RefList<LooseRef> curList = looseRefs.get(); final int idx = curList.find(name); if (0 <= idx) { final LooseRef o = curList.get(idx); final LooseRef n = scanRef(o, name); if (n == null) { if (looseRefs.compareAndSet(curList, curList.remove(idx))) modCnt.incrementAndGet(); return packed.get(name); } if (o == n) return n; if (looseRefs.compareAndSet(curList, curList.set(idx, n))) modCnt.incrementAndGet(); return n; } final LooseRef n = scanRef(null, name); if (n == null) return packed.get(name); // check whether the found new ref is the an additional ref. These refs // should not go into looseRefs for (int i = 0; i < additionalRefsNames.length; i++) if (name.equals(additionalRefsNames[i])) return n; if (looseRefs.compareAndSet(curList, curList.add(idx, n))) modCnt.incrementAndGet(); return n; }
void scan(String prefix) { if (ALL.equals(prefix)) { scanOne(HEAD); scanTree(R_REFS, refsDir); // If any entries remain, they are deleted, drop them. if (newLoose == null && curIdx < curLoose.size()) newLoose = curLoose.copy(curIdx); } else if (prefix.startsWith(R_REFS) && prefix.endsWith("/")) { // $NON-NLS-1$ curIdx = -(curLoose.find(prefix) + 1); File dir = new File(refsDir, prefix.substring(R_REFS.length())); scanTree(prefix, dir); // Skip over entries still within the prefix; these have // been removed from the directory. while (curIdx < curLoose.size()) { if (!curLoose.get(curIdx).getName().startsWith(prefix)) break; if (newLoose == null) newLoose = curLoose.copy(curIdx); curIdx++; } // Keep any entries outside of the prefix space, we // do not know anything about their status. if (newLoose != null) { while (curIdx < curLoose.size()) newLoose.add(curLoose.get(curIdx++)); } } }
@Override public Map<String, Ref> getRefs(String prefix) throws IOException { final RefList<LooseRef> oldLoose = looseRefs.get(); LooseScanner scan = new LooseScanner(oldLoose); scan.scan(prefix); final RefList<Ref> packed = getPackedRefs(); RefList<LooseRef> loose; if (scan.newLoose != null) { scan.newLoose.sort(); loose = scan.newLoose.toRefList(); if (looseRefs.compareAndSet(oldLoose, loose)) modCnt.incrementAndGet(); } else loose = oldLoose; fireRefsChanged(); RefList.Builder<Ref> symbolic = scan.symbolic; for (int idx = 0; idx < symbolic.size(); ) { final Ref symbolicRef = symbolic.get(idx); final Ref resolvedRef = resolve(symbolicRef, 0, prefix, loose, packed); if (resolvedRef != null && resolvedRef.getObjectId() != null) { symbolic.set(idx, resolvedRef); idx++; } else { // A broken symbolic reference, we have to drop it from the // collections the client is about to receive. Should be a // rare occurrence so pay a copy penalty. symbolic.remove(idx); final int toRemove = loose.find(symbolicRef.getName()); if (0 <= toRemove) loose = loose.remove(toRemove); } } symbolic.sort(); return new RefMap(prefix, packed, upcast(loose), symbolic.toRefList()); }
void removed(String refName) { RefCache oldCache, newCache; do { oldCache = cache.get(); if (oldCache == null) return; int p; RefList<DhtRef> ids = oldCache.ids; p = ids.find(refName); if (0 <= p) ids = ids.remove(p); RefList<DhtRef> sym = oldCache.sym; p = sym.find(refName); if (0 <= p) sym = sym.remove(p); newCache = new RefCache(ids, sym, oldCache.hints); } while (!cache.compareAndSet(oldCache, newCache)); }
/** * Obtain a modified copy of the cache with a ref stored. * * <p>This cache instance is not modified by this method. * * @param ref reference to add or replace. * @return a copy of this cache, with the reference added or replaced. */ public RefCache put(Ref ref) { RefList<Ref> newIds = this.ids.put(ref); RefList<Ref> newSym = this.sym; if (ref.isSymbolic()) { newSym = newSym.put(ref); } else { int p = newSym.find(ref.getName()); if (0 <= p) newSym = newSym.remove(p); } return new RefCache(newIds, newSym); }
@Override public boolean isNameConflicting(String refName) throws IOException { RefList<DhtRef> all = readRefs().ids; // Cannot be nested within an existing reference. int lastSlash = refName.lastIndexOf('/'); while (0 < lastSlash) { String needle = refName.substring(0, lastSlash); if (all.contains(needle)) return true; lastSlash = refName.lastIndexOf('/', lastSlash - 1); } // Cannot be the container of an existing reference. String prefix = refName + '/'; int idx = -(all.find(prefix) + 1); if (idx < all.size() && all.get(idx).getName().startsWith(prefix)) return true; return false; }
void stored(String refName, RefData newData) { DhtRef ref = fromData(refName, newData); RefCache oldCache, newCache; do { oldCache = cache.get(); if (oldCache == null) return; RefList<DhtRef> ids = oldCache.ids.put(ref); RefList<DhtRef> sym = oldCache.sym; if (ref.isSymbolic()) { sym = sym.put(ref); } else { int p = sym.find(refName); if (0 <= p) sym = sym.remove(p); } newCache = new RefCache(ids, sym, oldCache.hints); } while (!cache.compareAndSet(oldCache, newCache)); }
@Override public Ref peel(final Ref ref) throws IOException { final Ref leaf = ref.getLeaf(); if (leaf.isPeeled() || leaf.getObjectId() == null) return ref; ObjectIdRef newLeaf = doPeel(leaf); // Try to remember this peeling in the cache, so we don't have to do // it again in the future, but only if the reference is unchanged. if (leaf.getStorage().isLoose()) { RefList<LooseRef> curList = looseRefs.get(); int idx = curList.find(leaf.getName()); if (0 <= idx && curList.get(idx) == leaf) { LooseRef asPeeled = ((LooseRef) leaf).peel(newLeaf); RefList<LooseRef> newList = curList.set(idx, asPeeled); looseRefs.compareAndSet(curList, newList); } } return recreate(ref, newLeaf); }
void delete(RefDirectoryUpdate update) throws IOException { Ref dst = update.getRef(); String name = dst.getName(); // Write the packed-refs file using an atomic update. We might // wind up reading it twice, before and after the lock, to ensure // we don't miss an edit made externally. final PackedRefList packed = getPackedRefs(); if (packed.contains(name)) { LockFile lck = new LockFile(packedRefsFile); if (!lck.lock()) throw new LockFailedException(packedRefsFile); try { PackedRefList cur = readPackedRefs(); int idx = cur.find(name); if (0 <= idx) commitPackedRefs(lck, cur.remove(idx), packed); } finally { lck.unlock(); } } RefList<LooseRef> curLoose, newLoose; do { curLoose = looseRefs.get(); int idx = curLoose.find(name); if (idx < 0) break; newLoose = curLoose.remove(idx); } while (!looseRefs.compareAndSet(curLoose, newLoose)); int levels = levelsIn(name) - 2; delete(logWriter.logFor(name), levels); if (dst.getStorage().isLoose()) { update.unlock(); delete(fileFor(name), levels); } modCnt.incrementAndGet(); fireRefsChanged(); }
@Override public Map<String, Ref> getRefs(String prefix) throws IOException { RefCache curr = readRefs(); RefList<DhtRef> packed = RefList.emptyList(); RefList<DhtRef> loose = curr.ids; RefList.Builder<DhtRef> sym = new RefList.Builder<DhtRef>(curr.sym.size()); for (int idx = 0; idx < curr.sym.size(); idx++) { DhtRef ref = curr.sym.get(idx); String name = ref.getName(); ref = resolve(ref, 0, loose); if (ref != null && ref.getObjectId() != null) { sym.add(ref); } else { // A broken symbolic reference, we have to drop it from the // collections the client is about to receive. Should be a // rare occurrence so pay a copy penalty. int toRemove = loose.find(name); if (0 <= toRemove) loose = loose.remove(toRemove); } } return new RefMap(prefix, packed, loose, sym.toRefList()); }