do everything possible to cache the unchanged file content to revert in p4 offline mode (IDEA-79768)

This commit is contained in:
peter
2012-01-23 20:43:09 +01:00
parent b851d51d5d
commit bc141bfc74
4 changed files with 218 additions and 23 deletions
@@ -121,6 +121,7 @@ public class ContentRevisionCache {
}
}
@Nullable
public static String getOrLoadAsString(Project project,
FilePath file,
VcsRevisionNumber number,
@@ -135,7 +136,7 @@ public class ContentRevisionCache {
return CharsetToolkit.bytesToString(bytes, charset);
}
private static String bytesToString(FilePath path, byte[] bytes) {
private static String bytesToString(FilePath path, @NotNull byte[] bytes) {
Charset charset = null;
if (path.getVirtualFile() != null) {
charset = path.getVirtualFile().getCharset();
@@ -183,10 +184,13 @@ public class ContentRevisionCache {
return bytes;
}
@Nullable
public static String getOrLoadAsString(final Project project, FilePath path, VcsRevisionNumber number, @NotNull VcsKey vcsKey,
@NotNull UniqueType type, final Throwable2Computable<byte[], VcsException, IOException> loader)
throws VcsException, IOException {
byte[] bytes = getOrLoadAsBytes(project, path, number, vcsKey, type, loader);
if (bytes == null) return null;
return bytesToString(path, bytes);
}
@@ -47,6 +47,7 @@ import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.EditorNotifications;
import com.intellij.util.*;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.continuation.ContinuationPause;
import com.intellij.util.messages.Topic;
@@ -447,6 +448,11 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
}
}
});
for (VcsDirtyScope scope : scopes) {
updateLastUnchangedContents(scope);
}
myChangesViewManager.scheduleRefresh();
}
catch (DisposedException e) {
@@ -620,6 +626,55 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
}
}
private void updateLastUnchangedContents(VcsDirtyScope scope) {
scope.iterateExistingInsideScope(new Processor<VirtualFile>() {
@Override
public boolean process(VirtualFile file) {
if (!file.isDirectory() && getStatus(file) == FileStatus.NOT_CHANGED) {
LastUnchangedContentTracker.updateLastUnchangedContent(file);
}
return true;
}
});
}
public void collectUnchangedFileContents(@Nullable ProgressIndicator indicator) {
final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
for (VcsDirectoryMapping mapping : vcsManager.getDirectoryMappings()) {
final AbstractVcs vcs = vcsManager.findVcsByName(mapping.getVcs());
final VirtualFile file = LocalFileSystem.getInstance().findFileByPath(mapping.getDirectory());
if (vcs != null && file != null) {
if (indicator != null) {
indicator.setText(mapping.getDirectory());
}
final VcsDirtyScopeImpl scope = new VcsDirtyScopeImpl(vcs, myProject);
scope.addDirtyDirRecursively(new FilePathImpl(file));
updateLastUnchangedContents(scope);
}
}
for (LocalChangeList list : getChangeListsCopy()) {
for (Change change : list.getChanges()) {
final VirtualFile file = change.getVirtualFile();
final ContentRevision before = change.getBeforeRevision();
if (file != null && before != null && !LastUnchangedContentTracker.hasSavedContent(file)) {
try {
final String content = before.getContent();
if (content != null) {
LastUnchangedContentTracker.forceSavedContent(file, content);
}
}
catch (VcsException e) {
LOG.info(e);
}
}
}
}
}
private void checkIfDisposed() {
if (myUpdater.isStopped()) throw new DisposedException();
}
@@ -1292,6 +1347,20 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
@TestOnly
public void waitUntilRefreshed() {
myUpdater.waitUntilRefreshed();
waitUpdateAlarm();
}
// this is for perforce tests to ensure that LastSuccessfulUpdateTracker receives the event it needs
private static void waitUpdateAlarm() {
final Semaphore semaphore = new Semaphore();
semaphore.down();
ourUpdateAlarm.execute(new Runnable() {
@Override
public void run() {
semaphore.up();
}
});
semaphore.waitFor();
}
/**
@@ -1301,6 +1370,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
final EnsureUpToDateFromNonAWTThread worker = new EnsureUpToDateFromNonAWTThread(myProject);
worker.execute();
myUpdater.waitUntilRefreshed();
waitUpdateAlarm();
return worker.isDone();
}
@@ -98,29 +98,10 @@ public class ChangeListWorker implements ChangeListsWriteOperations {
}
public void onAfterWorkerSwitch(@NotNull final ChangeListWorker previous) {
final boolean somethingChanged = myDelta.step(previous.myIdx, myIdx);
checkForMultipleCopiesNotMove();
checkForMultipleCopiesNotMove(myDelta.step(previous.myIdx, myIdx));
}
public boolean takeData(@NotNull final ChangeListWorker worker) {
myMap.clear();
myMap.putAll(worker.myMap);
myDefault = worker.myDefault;
myListsToDisappear.clear();
myListsToDisappear.addAll(worker.myListsToDisappear);
final boolean somethingChanged = myDelta.step(myIdx, worker.myIdx);
myIdx = new ChangeListsIndexes(worker.myIdx);
checkForMultipleCopiesNotMove();
myLocallyDeleted.takeFrom(worker.myLocallyDeleted);
mySwitchedHolder.takeFrom(worker.mySwitchedHolder);
return somethingChanged;
}
private void checkForMultipleCopiesNotMove() {
private void checkForMultipleCopiesNotMove(boolean somethingChanged) {
final MultiMap<FilePath, Pair<Change, String>> moves = new MultiMap<FilePath, Pair<Change, String>>() {
protected Collection<Pair<Change, String>> createCollection() {
return new LinkedList<Pair<Change, String>>();
@@ -139,7 +120,6 @@ public class ChangeListWorker implements ChangeListsWriteOperations {
}
}
}
boolean somethingChanged = false;
for (FilePath filePath : moves.keySet()) {
final List<Pair<Change, String>> copies = (List<Pair<Change, String>>) moves.get(filePath);
if (copies.size() == 1) continue;
@@ -0,0 +1,141 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs.changes;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.FileAttribute;
import com.intellij.openapi.vfs.newvfs.ManagingFS;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @author peter
*/
public class LastUnchangedContentTracker {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.LastUnchangedContentTracker");
private static final Key<Long> LAST_TS_KEY = Key.create("LAST_TS_KEY");
private static final FileAttribute LAST_TS_ATTR = new FileAttribute("LAST_TS_ATTR", 0, true);
private static final FileAttribute ACQUIRED_CONTENT_ATTR = new FileAttribute("ACQUIRED_CONTENT_ATTR", 0, false);
public static void updateLastUnchangedContent(@NotNull VirtualFile file) {
Long lastTs = getLastSavedStamp(file);
final long stamp = file.getModificationStamp();
if (lastTs != null && stamp == lastTs) {
return;
}
Integer oldContentId = getSavedContentId(file);
if (oldContentId != null) {
getFS().releaseContent(oldContentId);
}
saveContentReference(file, getFS().acquireContent(file));
}
@Nullable
public static byte[] getLastUnchangedContent(@NotNull VirtualFile file) {
final Integer id = getSavedContentId(file);
try {
return id == null ? null : getFS().contentsToByteArray(id);
}
catch (IOException e) {
LOG.info(e);
}
return null;
}
private static PersistentFS getFS() {
return (PersistentFS)ManagingFS.getInstance();
}
private static void saveContentReference(VirtualFile file, int contentId) {
if (ChangeListManagerImpl.DEBUG) {
System.out.println("LastUnchangedContentTracker.saveCurrentContent");
try {
System.out.println("content = " + VfsUtil.loadText(file));
}
catch (IOException e) {
e.printStackTrace();
}
}
long stamp = file.getModificationStamp();
try {
final DataOutputStream contentStream = ACQUIRED_CONTENT_ATTR.writeAttribute(file);
contentStream.writeInt(contentId);
contentStream.close();
final DataOutputStream tsStream = LAST_TS_ATTR.writeAttribute(file);
tsStream.writeLong(stamp);
tsStream.close();
file.putUserData(LAST_TS_KEY, stamp);
}
catch (IOException e) {
LOG.info(e);
}
}
public static boolean hasSavedContent(VirtualFile file) {
return getSavedContentId(file) != null;
}
public static void forceSavedContent(VirtualFile file, @NotNull String content) {
saveContentReference(file, getFS().storeUnlinkedContent(content.getBytes(file.getCharset())));
}
@Nullable
private static Integer getSavedContentId(VirtualFile file) {
Integer oldContentId = null;
try {
final DataInputStream stream = ACQUIRED_CONTENT_ATTR.readAttribute(file);
if (stream != null) {
oldContentId = stream.readInt();
stream.close();
}
}
catch (IOException e) {
LOG.info(e);
}
return oldContentId;
}
@Nullable
private static Long getLastSavedStamp(VirtualFile file) {
Long l = file.getUserData(LAST_TS_KEY);
if (l == null) {
try {
final DataInputStream stream = LAST_TS_ATTR.readAttribute(file);
if (stream != null) {
l = stream.readLong();
stream.close();
}
}
catch (IOException e) {
LOG.info(e);
}
}
return l;
}
}