Browse Source

Support force writing reflog on a per-update basis

Even if a repository has core.logAllRefUpdates=true, ReflogWriter does
not create reflog files unless the refs are under a hard-coded list of
prefixes, or unless the forceWrite bit is set. Expose the forceWrite bit
on a per-update basis in RefUpdate/BatchRefUpdate/ReceiveCommand,
creating RefLogWriters as necessary.

Change-Id: Ifc851fba00f76bf56d4134f821d0576b37810f80
stable-4.9
Dave Borowitz 7 years ago committed by David Pursehouse
parent
commit
77a28e0d58
  1. 79
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
  2. 2
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
  3. 4
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java
  4. 13
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
  5. 4
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
  6. 50
      org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
  7. 32
      org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
  8. 32
      org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java

79
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java

@ -142,11 +142,7 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase {
super.setUp(); super.setUp();
diskRepo = createBareRepository(); diskRepo = createBareRepository();
StoredConfig cfg = diskRepo.getConfig(); setLogAllRefUpdates(true);
cfg.load();
cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
cfg.save();
refdir = (RefDirectory) diskRepo.getRefDatabase(); refdir = (RefDirectory) diskRepo.getRefDatabase();
refdir.setRetrySleepMs(Arrays.asList(0, 0)); refdir.setRetrySleepMs(Arrays.asList(0, 0));
@ -655,6 +651,71 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase {
getLastReflog("refs/heads/branch")); getLastReflog("refs/heads/branch"));
} }
@Test
public void refLogNotWrittenWithoutConfigOption() throws Exception {
setLogAllRefUpdates(false);
writeRef("refs/heads/master", A);
Map<String, ReflogEntry> oldLogs =
getLastReflogs("refs/heads/master", "refs/heads/branch");
assertTrue(oldLogs.isEmpty());
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
assertResults(cmds, OK, OK);
assertReflogUnchanged(oldLogs, "refs/heads/master");
assertReflogUnchanged(oldLogs, "refs/heads/branch");
}
@Test
public void forceRefLogInUpdate() throws Exception {
setLogAllRefUpdates(false);
writeRef("refs/heads/master", A);
assertTrue(
getLastReflogs("refs/heads/master", "refs/heads/branch").isEmpty());
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
execute(
newBatchUpdate(cmds)
.setRefLogMessage("a reflog", false)
.setForceRefLog(true));
assertResults(cmds, OK, OK);
assertReflogEquals(
reflog(A, B, new PersonIdent(diskRepo), "a reflog"),
getLastReflog("refs/heads/master"));
assertReflogEquals(
reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
getLastReflog("refs/heads/branch"));
}
@Test
public void forceRefLogInCommand() throws Exception {
setLogAllRefUpdates(false);
writeRef("refs/heads/master", A);
Map<String, ReflogEntry> oldLogs =
getLastReflogs("refs/heads/master", "refs/heads/branch");
assertTrue(oldLogs.isEmpty());
List<ReceiveCommand> cmds = Arrays.asList(
new ReceiveCommand(A, B, "refs/heads/master", UPDATE),
new ReceiveCommand(zeroId(), B, "refs/heads/branch", CREATE));
cmds.get(1).setForceRefLog(true);
execute(newBatchUpdate(cmds).setRefLogMessage("a reflog", false));
assertResults(cmds, OK, OK);
assertReflogUnchanged(oldLogs, "refs/heads/master");
assertReflogEquals(
reflog(zeroId(), B, new PersonIdent(diskRepo), "a reflog"),
getLastReflog("refs/heads/branch"));
}
@Test @Test
public void packedRefsLockFailure() throws Exception { public void packedRefsLockFailure() throws Exception {
writeLooseRef("refs/heads/master", A); writeLooseRef("refs/heads/master", A);
@ -791,6 +852,14 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase {
"refs/heads/branch", B); "refs/heads/branch", B);
} }
private void setLogAllRefUpdates(boolean enable) throws Exception {
StoredConfig cfg = diskRepo.getConfig();
cfg.load();
cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, enable);
cfg.save();
}
private void writeLooseRef(String name, AnyObjectId id) throws IOException { private void writeLooseRef(String name, AnyObjectId id) throws IOException {
write(new File(diskRepo.getDirectory(), name), id.name() + "\n"); write(new File(diskRepo.getDirectory(), name), id.name() + "\n");
} }

2
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java

@ -1018,7 +1018,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
RefDirectory refs = (RefDirectory) db.getRefDatabase(); RefDirectory refs = (RefDirectory) db.getRefDatabase();
RefDirectoryUpdate update = refs.newUpdate(refName, true); RefDirectoryUpdate update = refs.newUpdate(refName, true);
update.setNewObjectId(newId); update.setNewObjectId(newId);
refs.log(update, msg, true); refs.log(false, update, msg, true);
} }
private static class SubclassedId extends ObjectId { private static class SubclassedId extends ObjectId {

4
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java

@ -406,7 +406,6 @@ class PackedBatchRefUpdate extends BatchRefUpdate {
if (ident == null) { if (ident == null) {
ident = new PersonIdent(refdb.getRepository()); ident = new PersonIdent(refdb.getRepository());
} }
ReflogWriter w = refdb.getLogWriter();
for (ReceiveCommand cmd : commands) { for (ReceiveCommand cmd : commands) {
// Assume any pending commands have already been executed atomically. // Assume any pending commands have already been executed atomically.
if (cmd.getResult() != ReceiveCommand.Result.OK) { if (cmd.getResult() != ReceiveCommand.Result.OK) {
@ -436,7 +435,8 @@ class PackedBatchRefUpdate extends BatchRefUpdate {
} }
} }
try { try {
w.log(name, cmd.getOldId(), cmd.getNewId(), ident, msg); new ReflogWriter(refdb, isForceRefLog(cmd))
.log(name, cmd.getOldId(), cmd.getNewId(), ident, msg);
} catch (IOException e) { } catch (IOException e) {
// Ignore failures, but continue attempting to write more reflogs. // Ignore failures, but continue attempting to write more reflogs.
// //

13
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java

@ -151,8 +151,6 @@ public class RefDirectory extends RefDatabase {
final File refsDir; final File refsDir;
private final ReflogWriter logWriter;
final File packedRefsFile; final File packedRefsFile;
final File logsDir; final File logsDir;
@ -210,7 +208,6 @@ public class RefDirectory extends RefDatabase {
final FS fs = db.getFS(); final FS fs = db.getFS();
parent = db; parent = db;
gitDir = db.getDirectory(); gitDir = db.getDirectory();
logWriter = new ReflogWriter(this);
refsDir = fs.resolve(gitDir, R_REFS); refsDir = fs.resolve(gitDir, R_REFS);
logsDir = fs.resolve(gitDir, LOGS); logsDir = fs.resolve(gitDir, LOGS);
logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS); logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS);
@ -224,8 +221,8 @@ public class RefDirectory extends RefDatabase {
return parent; return parent;
} }
ReflogWriter getLogWriter() { ReflogWriter newLogWriter(boolean force) {
return logWriter; return new ReflogWriter(this, force);
} }
/** /**
@ -249,7 +246,7 @@ public class RefDirectory extends RefDatabase {
FileUtils.mkdir(refsDir); FileUtils.mkdir(refsDir);
FileUtils.mkdir(new File(refsDir, R_HEADS.substring(R_REFS.length()))); FileUtils.mkdir(new File(refsDir, R_HEADS.substring(R_REFS.length())));
FileUtils.mkdir(new File(refsDir, R_TAGS.substring(R_REFS.length()))); FileUtils.mkdir(new File(refsDir, R_TAGS.substring(R_REFS.length())));
logWriter.create(); newLogWriter(false).create();
} }
@Override @Override
@ -866,9 +863,9 @@ public class RefDirectory extends RefDatabase {
} }
} }
void log(final RefUpdate update, final String msg, final boolean deref) void log(boolean force, RefUpdate update, String msg, boolean deref)
throws IOException { throws IOException {
logWriter.log(update, msg, deref); newLogWriter(force).log(update, msg, deref);
} }
private Ref resolve(final Ref ref, int depth, String prefix, private Ref resolve(final Ref ref, int depth, String prefix,

4
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java

@ -120,7 +120,7 @@ class RefDirectoryUpdate extends RefUpdate {
msg = strResult; msg = strResult;
} }
} }
database.log(this, msg, shouldDeref); database.log(isForceRefLog(), this, msg, shouldDeref);
} }
if (!lock.commit()) if (!lock.commit())
return Result.LOCK_FAILURE; return Result.LOCK_FAILURE;
@ -159,7 +159,7 @@ class RefDirectoryUpdate extends RefUpdate {
String msg = getRefLogMessage(); String msg = getRefLogMessage();
if (msg != null) if (msg != null)
database.log(this, msg, false); database.log(isForceRefLog(), this, msg, false);
if (!lock.commit()) if (!lock.commit())
return Result.LOCK_FAILURE; return Result.LOCK_FAILURE;
database.storedSymbolicRef(this, lock.getCommitSnapshot(), target); database.storedSymbolicRef(this, lock.getCommitSnapshot(), target);

50
org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java

@ -104,6 +104,12 @@ public class BatchRefUpdate {
/** Should the result value be appended to {@link #refLogMessage}. */ /** Should the result value be appended to {@link #refLogMessage}. */
private boolean refLogIncludeResult; private boolean refLogIncludeResult;
/**
* Should reflogs be written even if the configured default for this ref is
* not to write it.
*/
private boolean forceRefLog;
/** Push certificate associated with this update. */ /** Push certificate associated with this update. */
private PushCertificate pushCert; private PushCertificate pushCert;
@ -198,6 +204,12 @@ public class BatchRefUpdate {
/** /**
* Set the message to include in the reflog. * Set the message to include in the reflog.
* <p> * <p>
* Repository implementations may limit which reflogs are written by default,
* based on the project configuration. If a repo is not configured to write
* logs for this ref by default, setting the message alone may have no effect.
* To indicate that the repo should write logs for this update in spite of
* configured defaults, use {@link #setForceRefLog(boolean)}.
* <p>
* Describes the default for commands in this batch that do not override it * Describes the default for commands in this batch that do not override it
* with {@link ReceiveCommand#setRefLogMessage(String, boolean)}. * with {@link ReceiveCommand#setRefLogMessage(String, boolean)}.
* *
@ -235,6 +247,18 @@ public class BatchRefUpdate {
return this; return this;
} }
/**
* Force writing a reflog for the updated ref.
*
* @param force whether to force.
* @return {@code this}
* @since 4.9
*/
public BatchRefUpdate setForceRefLog(boolean force) {
forceRefLog = force;
return this;
}
/** /**
* Check whether log has been disabled by {@link #disableRefLog()}. * Check whether log has been disabled by {@link #disableRefLog()}.
* *
@ -244,6 +268,16 @@ public class BatchRefUpdate {
return refLogMessage == null; return refLogMessage == null;
} }
/**
* Check whether the reflog should be written regardless of repo defaults.
*
* @return whether force writing is enabled.
* @since 4.9
*/
protected boolean isForceRefLog() {
return forceRefLog;
}
/** /**
* Request that all updates in this batch be performed atomically. * Request that all updates in this batch be performed atomically.
* <p> * <p>
@ -635,6 +669,7 @@ public class BatchRefUpdate {
} else { } else {
ru.setRefLogIdent(refLogIdent); ru.setRefLogIdent(refLogIdent);
ru.setRefLogMessage(getRefLogMessage(cmd), isRefLogIncludingResult(cmd)); ru.setRefLogMessage(getRefLogMessage(cmd), isRefLogIncludingResult(cmd));
ru.setForceRefLog(isForceRefLog(cmd));
} }
ru.setPushCertificate(pushCert); ru.setPushCertificate(pushCert);
switch (cmd.getType()) { switch (cmd.getType()) {
@ -696,6 +731,21 @@ public class BatchRefUpdate {
? cmd.isRefLogIncludingResult() : isRefLogIncludingResult(); ? cmd.isRefLogIncludingResult() : isRefLogIncludingResult();
} }
/**
* Check whether the reflog for a command should be written regardless of repo
* defaults.
*
* @param cmd
* specific command.
* @return whether force writing is enabled.
* @since 4.9
*/
protected boolean isForceRefLog(ReceiveCommand cmd) {
Boolean isForceRefLog = cmd.isForceRefLog();
return isForceRefLog != null ? isForceRefLog.booleanValue()
: isForceRefLog();
}
@Override @Override
public String toString() { public String toString() {
StringBuilder r = new StringBuilder(); StringBuilder r = new StringBuilder();

32
org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java

@ -185,6 +185,12 @@ public abstract class RefUpdate {
/** Should the Result value be appended to {@link #refLogMessage}. */ /** Should the Result value be appended to {@link #refLogMessage}. */
private boolean refLogIncludeResult; private boolean refLogIncludeResult;
/**
* Should reflogs be written even if the configured default for this ref is
* not to write it.
*/
private boolean forceRefLog;
/** Old value of the ref, obtained after we lock it. */ /** Old value of the ref, obtained after we lock it. */
private ObjectId oldValue; private ObjectId oldValue;
@ -403,6 +409,12 @@ public abstract class RefUpdate {
/** /**
* Set the message to include in the reflog. * Set the message to include in the reflog.
* <p>
* Repository implementations may limit which reflogs are written by default,
* based on the project configuration. If a repo is not configured to write
* logs for this ref by default, setting the message alone may have no effect.
* To indicate that the repo should write logs for this update in spite of
* configured defaults, use {@link #setForceRefLog(boolean)}.
* *
* @param msg * @param msg
* the message to describe this change. It may be null if * the message to describe this change. It may be null if
@ -430,6 +442,26 @@ public abstract class RefUpdate {
refLogIncludeResult = false; refLogIncludeResult = false;
} }
/**
* Force writing a reflog for the updated ref.
*
* @param force whether to force.
* @since 4.9
*/
public void setForceRefLog(boolean force) {
forceRefLog = force;
}
/**
* Check whether the reflog should be written regardless of repo defaults.
*
* @return whether force writing is enabled.
* @since 4.9
*/
protected boolean isForceRefLog() {
return forceRefLog;
}
/** /**
* The old value of the ref, prior to the update being attempted. * The old value of the ref, prior to the update being attempted.
* <p> * <p>

32
org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java

@ -225,6 +225,8 @@ public class ReceiveCommand {
private boolean refLogIncludeResult; private boolean refLogIncludeResult;
private Boolean forceRefLog;
private boolean typeIsCorrect; private boolean typeIsCorrect;
/** /**
@ -390,8 +392,22 @@ public class ReceiveCommand {
} }
/** /**
* Check whether this command has a custom reflog setting that should override * Force writing a reflog for the updated ref.
* defaults in any containing {@link org.eclipse.jgit.lib.BatchRefUpdate}. *
* @param force whether to force.
* @since 4.9
*/
public void setForceRefLog(boolean force) {
forceRefLog = Boolean.valueOf(force);
}
/**
* Check whether this command has a custom reflog message setting that should
* override defaults in any containing
* {@link org.eclipse.jgit.lib.BatchRefUpdate}.
* <p>
* Does not take into account whether {@code #setForceRefLog(boolean)} has
* been called.
* *
* @return whether a custom reflog is set. * @return whether a custom reflog is set.
* @since 4.9 * @since 4.9
@ -433,6 +449,18 @@ public class ReceiveCommand {
return refLogIncludeResult; return refLogIncludeResult;
} }
/**
* Check whether the reflog should be written regardless of repo defaults.
*
* @return whether force writing is enabled; null if {@code
* #setForceRefLog(boolean)} was never called.
* @since 4.9
*/
@Nullable
public Boolean isForceRefLog() {
return forceRefLog;
}
/** /**
* Set the status of this command. * Set the status of this command.
* *

Loading…
Cancel
Save