/*
- * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008-2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
private void doCheckout(final Ref branch) throws IOException {
if (branch == null)
throw die("cannot checkout; no HEAD advertised by remote");
- if (!Constants.HEAD.equals(branch.getName()))
- db.writeSymref(Constants.HEAD, branch.getName());
+ if (!Constants.HEAD.equals(branch.getName())) {
+ RefUpdate u = db.updateRef(Constants.HEAD);
+ u.disableRefLog();
+ u.link(branch.getName());
+ }
final Commit commit = db.mapCommit(branch.getObjectId());
final RefUpdate u = db.updateRef(Constants.HEAD);
/*
+ * Copyright (C) 2009-2010, Google Inc.
* Copyright (C) 2009, Robin Rosenberg
* Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
* and other copyright owners as documented in the project's IP log.
*/
public class RefTest extends SampleDataRepositoryTestCase {
+ private void writeSymref(String src, String dst) throws IOException {
+ RefUpdate u = db.updateRef(src);
+ switch (u.link(dst)) {
+ case NEW:
+ case FORCED:
+ case NO_CHANGE:
+ break;
+ default:
+ fail("link " + src + " to " + dst);
+ }
+ }
+
public void testReadAllIncludingSymrefs() throws Exception {
ObjectId masterId = db.resolve("refs/heads/master");
RefUpdate updateRef = db.updateRef("refs/remotes/origin/master");
updateRef.setNewObjectId(masterId);
updateRef.setForceUpdate(true);
updateRef.update();
- db
- .writeSymref("refs/remotes/origin/HEAD",
- "refs/remotes/origin/master");
+ writeSymref("refs/remotes/origin/HEAD",
+ "refs/remotes/origin/master");
ObjectId r = db.resolve("refs/remotes/origin/HEAD");
assertEquals(masterId, r);
}
public void testReadSymRefToPacked() throws IOException {
- db.writeSymref("HEAD", "refs/heads/b");
+ writeSymref("HEAD", "refs/heads/b");
Ref ref = db.getRef("HEAD");
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
assertTrue("is symref", ref.isSymbolic());
Result update = updateRef.update();
assertEquals(Result.FORCED, update); // internal
- db.writeSymref("HEAD", "refs/heads/master");
+ writeSymref("HEAD", "refs/heads/master");
Ref ref = db.getRef("HEAD");
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
ref = ref.getTarget();
/*
* Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
+ * Copyright (C) 2009-2010, Google Inc.
* Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
* and other copyright owners as documented in the project's IP log.
*
public class RefUpdateTest extends SampleDataRepositoryTestCase {
+ private void writeSymref(String src, String dst) throws IOException {
+ RefUpdate u = db.updateRef(src);
+ switch (u.link(dst)) {
+ case NEW:
+ case FORCED:
+ case NO_CHANGE:
+ break;
+ default:
+ fail("link " + src + " to " + dst);
+ }
+ }
+
private RefUpdate updateRef(final String name) throws IOException {
final RefUpdate ref = db.updateRef(name);
ref.setNewObjectId(db.resolve(Constants.HEAD));
*/
public void testUpdateRefDetachedUnbornHead() throws Exception {
ObjectId ppid = db.resolve("refs/heads/master^");
- db.writeSymref("HEAD", "refs/heads/unborn");
+ writeSymref("HEAD", "refs/heads/unborn");
RefUpdate updateRef = db.updateRef("HEAD", true);
updateRef.setForceUpdate(true);
updateRef.setNewObjectId(ppid);
// Do not use the defalt repo for this case.
Map<String, Ref> allRefs = db.getAllRefs();
ObjectId oldValue = db.resolve("HEAD");
- db.writeSymref(Constants.HEAD, "refs/heads/newref");
+ writeSymref(Constants.HEAD, "refs/heads/newref");
RefUpdate updateRef = db.updateRef(Constants.HEAD);
updateRef.setForceUpdate(true);
updateRef.setNewObjectId(oldValue);
public void testRenameCurrentBranch() throws IOException {
ObjectId rb = db.resolve("refs/heads/b");
- db.writeSymref(Constants.HEAD, "refs/heads/b");
+ writeSymref(Constants.HEAD, "refs/heads/b");
ObjectId oldHead = db.resolve(Constants.HEAD);
assertTrue("internal test condition, b == HEAD", rb.equals(oldHead));
writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
public void tryRenameWhenLocked(String toLock, String fromName,
String toName, String headPointsTo) throws IOException {
// setup
- db.writeSymref(Constants.HEAD, headPointsTo);
+ writeSymref(Constants.HEAD, headPointsTo);
ObjectId oldfromId = db.resolve(fromName);
ObjectId oldHeadId = db.resolve(Constants.HEAD);
writeReflog(db, oldfromId, oldfromId, "Just a message",
public void testRenameRefNameColission1avoided() throws IOException {
// setup
ObjectId rb = db.resolve("refs/heads/b");
- db.writeSymref(Constants.HEAD, "refs/heads/a");
+ writeSymref(Constants.HEAD, "refs/heads/a");
RefUpdate updateRef = db.updateRef("refs/heads/a");
updateRef.setNewObjectId(rb);
updateRef.setRefLogMessage("Setup", false);
public void testRenameRefNameColission2avoided() throws IOException {
// setup
ObjectId rb = db.resolve("refs/heads/b");
- db.writeSymref(Constants.HEAD, "refs/heads/prefix/a");
+ writeSymref(Constants.HEAD, "refs/heads/prefix/a");
RefUpdate updateRef = db.updateRef("refs/heads/prefix/a");
updateRef.setNewObjectId(rb);
updateRef.setRefLogMessage("Setup", false);
RefDirectoryUpdate update = refs.newUpdate(refName, true);
update.setOldObjectId(oldId);
update.setNewObjectId(newId);
- refs.log(update, msg);
+ refs.log(update, msg, true);
}
}
*/
public abstract boolean isNameConflicting(String name) throws IOException;
- /**
- * Create a symbolic reference from one name to another.
- *
- * @param name
- * the name of the reference. Should be {@link Constants#HEAD} or
- * starting with {@link Constants#R_REFS}.
- * @param target
- * the target of the reference.
- * @throws IOException
- * the reference could not be created or overwritten.
- */
- public abstract void link(String name, String target) throws IOException;
-
/**
* Create a new update command to create, modify or delete a reference.
*
return leaf;
}
- @Override
- public void link(String name, String target) throws IOException {
- LockFile lck = new LockFile(fileFor(name));
- if (!lck.lock())
- throw new IOException("Cannot lock " + name);
- lck.setNeedStatInformation(true);
- try {
- lck.write(encode(SYMREF + target + '\n'));
- if (!lck.commit())
- throw new IOException("Cannot write " + name);
- } finally {
- lck.unlock();
- }
- putLooseRef(newSymbolicRef(lck.getCommitLastModified(), name, target));
+ void storedSymbolicRef(RefDirectoryUpdate u, long modified, String target) {
+ putLooseRef(newSymbolicRef(modified, u.getRef().getName(), target));
fireRefsChanged();
}
- @Override
public RefDirectoryUpdate newUpdate(String name, boolean detach)
throws IOException {
final RefList<Ref> packed = getPackedRefs();
fireRefsChanged();
}
- void log(final RefUpdate update, final String msg) throws IOException {
+ void log(final RefUpdate update, final String msg, final boolean deref)
+ throws IOException {
final ObjectId oldId = update.getOldObjectId();
final ObjectId newId = update.getNewObjectId();
final Ref ref = update.getRef();
r.append('\n');
final byte[] rec = encode(r.toString());
- if (ref.isSymbolic())
+ if (deref && ref.isSymbolic()) {
+ log(ref.getName(), rec);
+ log(ref.getLeaf().getName(), rec);
+ } else {
log(ref.getName(), rec);
- log(ref.getLeaf().getName(), rec);
+ }
}
private void log(final String refName, final byte[] rec) throws IOException {
private boolean linkHEAD(RefUpdate target) {
try {
- refdb.link(Constants.HEAD, target.getName());
- return true;
+ RefUpdate u = refdb.newUpdate(Constants.HEAD, false);
+ u.disableRefLog();
+ switch (u.link(target.getName())) {
+ case NEW:
+ case FORCED:
+ case NO_CHANGE:
+ return true;
+ default:
+ return false;
+ }
} catch (IOException e) {
return false;
}
package org.eclipse.jgit.lib;
+import static org.eclipse.jgit.lib.Constants.encode;
+
import java.io.IOException;
/** Updates any reference stored by {@link RefDirectory}. */
}
@Override
- protected boolean tryLock() throws IOException {
- Ref dst = getRef().getLeaf();
+ protected boolean tryLock(boolean deref) throws IOException {
+ Ref dst = getRef();
+ if (deref)
+ dst = dst.getLeaf();
String name = dst.getName();
lock = new LockFile(database.fileFor(name));
if (lock.lock()) {
msg = strResult;
}
}
- database.log(this, msg);
+ database.log(this, msg, true);
}
if (!lock.commit())
return Result.LOCK_FAILURE;
database.delete(this);
return status;
}
+
+ @Override
+ protected Result doLink(final String target) throws IOException {
+ lock.setNeedStatInformation(true);
+ lock.write(encode(RefDirectory.SYMREF + target + '\n'));
+
+ String msg = getRefLogMessage();
+ if (msg != null)
+ database.log(this, msg, false);
+ if (!lock.commit())
+ return Result.LOCK_FAILURE;
+ database.storedSymbolicRef(this, lock.getCommitLastModified(), target);
+
+ if (getRef().getStorage() == Ref.Storage.NEW)
+ return Result.NEW;
+ return Result.FORCED;
+ }
}
* If the locking was successful the implementor must set the current
* identity value by calling {@link #setOldObjectId(ObjectId)}.
*
+ * @param deref
+ * true if the lock should be taken against the leaf level
+ * reference; false if it should be taken exactly against the
+ * current reference.
* @return true if the lock was acquired and the reference is likely
* protected from concurrent modification; false if it failed.
* @throws IOException
* the lock couldn't be taken due to an unexpected storage
* failure, and not because of a concurrent update.
*/
- protected abstract boolean tryLock() throws IOException;
+ protected abstract boolean tryLock(boolean deref) throws IOException;
/** Releases the lock taken by {@link #tryLock} if it succeeded. */
protected abstract void unlock();
*/
protected abstract Result doDelete(Result desiredResult) throws IOException;
+ /**
+ * @param target
+ * @return {@link Result#NEW} on success.
+ * @throws IOException
+ */
+ protected abstract Result doLink(String target) throws IOException;
+
/**
* Get the name of the ref this update will operate on.
*
}
}
+ /**
+ * Replace this reference with a symbolic reference to another reference.
+ * <p>
+ * This exact reference (not its traversed leaf) is replaced with a symbolic
+ * reference to the requested name.
+ *
+ * @param target
+ * name of the new target for this reference. The new target name
+ * must be absolute, so it must begin with {@code refs/}.
+ * @return {@link Result#NEW} or {@link Result#FORCED} on success.
+ * @throws IOException
+ */
+ public Result link(String target) throws IOException {
+ if (!target.startsWith(Constants.R_REFS))
+ throw new IllegalArgumentException("Not " + Constants.R_REFS);
+ if (getRefDatabase().isNameConflicting(getName()))
+ return Result.LOCK_FAILURE;
+ try {
+ if (!tryLock(false))
+ return Result.LOCK_FAILURE;
+
+ final Ref old = getRefDatabase().getRef(getName());
+ if (old != null && old.isSymbolic()) {
+ final Ref dst = old.getTarget();
+ if (target.equals(dst.getName()))
+ return result = Result.NO_CHANGE;
+ }
+
+ if (old != null && old.getObjectId() != null)
+ setOldObjectId(old.getObjectId());
+
+ final Ref dst = getRefDatabase().getRef(target);
+ if (dst != null && dst.getObjectId() != null)
+ setNewObjectId(dst.getObjectId());
+
+ return result = doLink(target);
+ } catch (IOException x) {
+ result = Result.IO_FAILURE;
+ throw x;
+ } finally {
+ unlock();
+ }
+ }
+
private Result updateImpl(final RevWalk walk, final Store store)
throws IOException {
RevObject newObj;
if (getRefDatabase().isNameConflicting(getName()))
return Result.LOCK_FAILURE;
try {
- if (!tryLock())
+ if (!tryLock(true))
return Result.LOCK_FAILURE;
if (expValue != null) {
final ObjectId o;
objectDatabase.create();
new File(gitDir, "branches").mkdir();
- final String master = Constants.R_HEADS + Constants.MASTER;
- refs.link(Constants.HEAD, master);
+
+ RefUpdate head = updateRef(Constants.HEAD);
+ head.disableRefLog();
+ head.link(Constants.R_HEADS + Constants.MASTER);
cfg.setInt("core", null, "repositoryformatversion", 0);
cfg.setBoolean("core", null, "filemode", true);
objectDatabase.openPack(pack, idx);
}
- /**
- * Writes a symref (e.g. HEAD) to disk
- *
- * @param name symref name
- * @param target pointed to ref
- * @throws IOException
- */
- public void writeSymref(final String name, final String target)
- throws IOException {
- refs.link(name, target);
- }
-
public String toString() {
return "Repository[" + getDirectory() + "]";
}