FileBasedConfig: Use FileSnapshot for isOutdated()
Relying only on the last modified time for a file can be tricky. The "racy git" problem may cause some modifications to be missed. Use the new FileSnapshot code to track when a configuration file has been modified, and needs to be reloaded in memory. Change-Id: Ib6312fdd3b2403eee5af3f8ae711294b0e5f9035 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
parent
c8db22f355
commit
3922e026e0
|
@ -58,6 +58,7 @@
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.StoredConfig;
|
import org.eclipse.jgit.lib.StoredConfig;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
import org.eclipse.jgit.util.IO;
|
import org.eclipse.jgit.util.IO;
|
||||||
|
@ -68,8 +69,9 @@
|
||||||
*/
|
*/
|
||||||
public class FileBasedConfig extends StoredConfig {
|
public class FileBasedConfig extends StoredConfig {
|
||||||
private final File configFile;
|
private final File configFile;
|
||||||
private volatile long lastModified;
|
|
||||||
private final FS fs;
|
private final FS fs;
|
||||||
|
private volatile FileSnapshot snapshot;
|
||||||
|
private volatile ObjectId hash;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a configuration with no default fallback.
|
* Create a configuration with no default fallback.
|
||||||
|
@ -99,6 +101,8 @@ public FileBasedConfig(Config base, File cfgLocation, FS fs) {
|
||||||
super(base);
|
super(base);
|
||||||
configFile = cfgLocation;
|
configFile = cfgLocation;
|
||||||
this.fs = fs;
|
this.fs = fs;
|
||||||
|
this.snapshot = FileSnapshot.DIRTY;
|
||||||
|
this.hash = ObjectId.zeroId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -125,11 +129,24 @@ public final File getFile() {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void load() throws IOException, ConfigInvalidException {
|
public void load() throws IOException, ConfigInvalidException {
|
||||||
lastModified = getFile().lastModified();
|
final FileSnapshot oldSnapshot = snapshot;
|
||||||
|
final FileSnapshot newSnapshot = FileSnapshot.save(getFile());
|
||||||
try {
|
try {
|
||||||
fromText(RawParseUtils.decode(IO.readFully(getFile())));
|
final byte[] in = IO.readFully(getFile());
|
||||||
|
final ObjectId newHash = hash(in);
|
||||||
|
if (hash.equals(newHash)) {
|
||||||
|
if (oldSnapshot.equals(newSnapshot))
|
||||||
|
oldSnapshot.setClean(newSnapshot);
|
||||||
|
else
|
||||||
|
snapshot = newSnapshot;
|
||||||
|
} else {
|
||||||
|
fromText(RawParseUtils.decode(in));
|
||||||
|
snapshot = newSnapshot;
|
||||||
|
hash = newHash;
|
||||||
|
}
|
||||||
} catch (FileNotFoundException noFile) {
|
} catch (FileNotFoundException noFile) {
|
||||||
clear();
|
clear();
|
||||||
|
snapshot = newSnapshot;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
final IOException e2 = new IOException(MessageFormat.format(JGitText.get().cannotReadFile, getFile()));
|
final IOException e2 = new IOException(MessageFormat.format(JGitText.get().cannotReadFile, getFile()));
|
||||||
e2.initCause(e);
|
e2.initCause(e);
|
||||||
|
@ -157,18 +174,29 @@ public void save() throws IOException {
|
||||||
if (!lf.lock())
|
if (!lf.lock())
|
||||||
throw new IOException(MessageFormat.format(JGitText.get().cannotLockFile, getFile()));
|
throw new IOException(MessageFormat.format(JGitText.get().cannotLockFile, getFile()));
|
||||||
try {
|
try {
|
||||||
lf.setNeedStatInformation(true);
|
lf.setNeedSnapshot(true);
|
||||||
lf.write(out);
|
lf.write(out);
|
||||||
if (!lf.commit())
|
if (!lf.commit())
|
||||||
throw new IOException(MessageFormat.format(JGitText.get().cannotCommitWriteTo, getFile()));
|
throw new IOException(MessageFormat.format(JGitText.get().cannotCommitWriteTo, getFile()));
|
||||||
} finally {
|
} finally {
|
||||||
lf.unlock();
|
lf.unlock();
|
||||||
}
|
}
|
||||||
lastModified = lf.getCommitLastModified();
|
snapshot = lf.getCommitSnapshot();
|
||||||
|
hash = hash(out);
|
||||||
// notify the listeners
|
// notify the listeners
|
||||||
fireConfigChangedEvent();
|
fireConfigChangedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
hash = hash(new byte[0]);
|
||||||
|
super.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ObjectId hash(final byte[] rawText) {
|
||||||
|
return ObjectId.fromRaw(Constants.newMessageDigest().digest(rawText));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getSimpleName() + "[" + getFile().getPath() + "]";
|
return getClass().getSimpleName() + "[" + getFile().getPath() + "]";
|
||||||
|
@ -179,6 +207,6 @@ public String toString() {
|
||||||
* than the file on disk
|
* than the file on disk
|
||||||
*/
|
*/
|
||||||
public boolean isOutdated() {
|
public boolean isOutdated() {
|
||||||
return getFile().lastModified() != lastModified;
|
return snapshot.isModified(getFile());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,10 @@ private FileSnapshot(long read, long modified) {
|
||||||
this.cannotBeRacilyClean = notRacyClean(read);
|
this.cannotBeRacilyClean = notRacyClean(read);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long lastModified() {
|
||||||
|
return lastModified;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the path has been modified since the snapshot was saved.
|
* Check if the path has been modified since the snapshot was saved.
|
||||||
*
|
*
|
||||||
|
|
|
@ -89,11 +89,11 @@ public boolean accept(File dir, String name) {
|
||||||
|
|
||||||
private FileOutputStream os;
|
private FileOutputStream os;
|
||||||
|
|
||||||
private boolean needStatInformation;
|
private boolean needSnapshot;
|
||||||
|
|
||||||
private boolean fsync;
|
private boolean fsync;
|
||||||
|
|
||||||
private long commitLastModified;
|
private FileSnapshot commitSnapshot;
|
||||||
|
|
||||||
private final FS fs;
|
private final FS fs;
|
||||||
|
|
||||||
|
@ -334,12 +334,24 @@ private void requireLock() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that {@link #commit()} remember modification time.
|
* Request that {@link #commit()} remember modification time.
|
||||||
|
* <p>
|
||||||
|
* This is an alias for {@code setNeedSnapshot(true)}.
|
||||||
*
|
*
|
||||||
* @param on
|
* @param on
|
||||||
* true if the commit method must remember the modification time.
|
* true if the commit method must remember the modification time.
|
||||||
*/
|
*/
|
||||||
public void setNeedStatInformation(final boolean on) {
|
public void setNeedStatInformation(final boolean on) {
|
||||||
needStatInformation = on;
|
setNeedSnapshot(on);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request that {@link #commit()} remember the {@link FileSnapshot}.
|
||||||
|
*
|
||||||
|
* @param on
|
||||||
|
* true if the commit method must remember the FileSnapshot.
|
||||||
|
*/
|
||||||
|
public void setNeedSnapshot(final boolean on) {
|
||||||
|
needSnapshot = on;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -442,8 +454,8 @@ private boolean renameLock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveStatInformation() {
|
private void saveStatInformation() {
|
||||||
if (needStatInformation)
|
if (needSnapshot)
|
||||||
commitLastModified = lck.lastModified();
|
commitSnapshot = FileSnapshot.save(lck);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -452,7 +464,12 @@ private void saveStatInformation() {
|
||||||
* @return modification time of the lock file right before we committed it.
|
* @return modification time of the lock file right before we committed it.
|
||||||
*/
|
*/
|
||||||
public long getCommitLastModified() {
|
public long getCommitLastModified() {
|
||||||
return commitLastModified;
|
return commitSnapshot.lastModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return get the {@link FileSnapshot} just before commit. */
|
||||||
|
public FileSnapshot getCommitSnapshot() {
|
||||||
|
return commitSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue