do everything possible to cache the unchanged file content to revert in p4 offline...
authorpeter <peter@jetbrains.com>
Mon, 23 Jan 2012 19:42:33 +0000 (20:42 +0100)
committerpeter <peter@jetbrains.com>
Mon, 23 Jan 2012 19:43:09 +0000 (20:43 +0100)
platform/vcs-api/src/com/intellij/openapi/vcs/impl/ContentRevisionCache.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LastUnchangedContentTracker.java [new file with mode: 0644]

index 3059bada685cd607fa4ab638aefa972f695d77b0..6f5c86c0a7d62d9776cbd9df1ac5d29456c3cbf5 100644 (file)
@@ -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);
   }
 
index 2973bd38895a6052e28c72b5f0bf3ae3120ef66f..6d2c63326af61599d38ae017c81b902888f23e2a 100644 (file)
@@ -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();
   }
 
index 2d99fd1b1354727fcd9a08fb55f31f31ad035570..a5fbc72c29b906882c5ed0094c1c7856adbe7434 100644 (file)
@@ -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;
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LastUnchangedContentTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LastUnchangedContentTracker.java
new file mode 100644 (file)
index 0000000..f94da58
--- /dev/null
@@ -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;
+  }
+}