[lvcs]: implement rollback to Label from local history

* add label ID to identify label change;
* create a local history model;
* implement revert method by label;
* inform if failed;
* tests added;
This commit is contained in:
Nadya Zabrodina
2016-01-25 17:05:16 +03:00
parent f76d299c94
commit 4fcd0f9d62
8 changed files with 210 additions and 0 deletions
@@ -18,10 +18,18 @@ package com.intellij.history;
public interface Label {
Label NULL_INSTANCE = new Label() {
@Override
public long getLabelChangeId() {
return -1;
}
public ByteContent getByteContent(String path) {
return null;
}
};
long getLabelChangeId();
ByteContent getByteContent(String path);
}
@@ -19,5 +19,7 @@ import com.intellij.history.ByteContent;
import com.intellij.history.core.tree.RootEntry;
public interface LabelImpl {
long getLabelChangeId();
ByteContent getByteContent(RootEntry root, String path);
}
@@ -105,6 +105,12 @@ public class LocalHistoryFacade {
private LabelImpl putLabel(@NotNull final PutLabelChange c) {
addChange(c);
return new LabelImpl() {
@Override
public long getLabelChangeId() {
return c.getId();
}
@Override
public ByteContent getByteContent(RootEntry root, String path) {
return getByteContentBefore(root, path, c);
@@ -108,4 +108,8 @@ public class ChangeRevision extends Revision {
public String toString() {
return getClass().getSimpleName() + ": " + myChangeToRevert;
}
public boolean containsChangeWithId(long id) {
return myChangeToRevert.getId() == id;
}
}
@@ -19,6 +19,9 @@ package com.intellij.history.integration;
import com.intellij.history.*;
import com.intellij.history.core.*;
import com.intellij.history.core.tree.RootEntry;
import com.intellij.history.integration.ui.models.DirectoryHistoryDialogModel;
import com.intellij.history.integration.ui.models.EntireFileHistoryDialogModel;
import com.intellij.history.integration.ui.models.HistoryDialogModel;
import com.intellij.history.utils.LocalHistoryLog;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
@@ -38,8 +41,11 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.intellij.history.integration.LocalHistoryUtil.findRevisionIndexToRevert;
public class LocalHistoryImpl extends LocalHistory implements ApplicationComponent {
private ChangeList myChangeList;
private LocalHistoryFacade myVcs;
@@ -183,6 +189,11 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
private Label label(final LabelImpl impl) {
return new Label() {
@Override
public long getLabelChangeId() {
return impl.getLabelChangeId();
}
@Override
public ByteContent getByteContent(final String path) {
return ApplicationManager.getApplication().runReadAction(new Computable<ByteContent>() {
@@ -237,5 +248,21 @@ public class LocalHistoryImpl extends LocalHistory implements ApplicationCompone
@Override
public void revertToLabel(@NotNull Project project, @NotNull VirtualFile f, @NotNull Label label) {
HistoryDialogModel dirHistoryModel = f.isDirectory()
? new DirectoryHistoryDialogModel(project, myGateway, myVcs, f)
: new EntireFileHistoryDialogModel(project, myGateway, myVcs, f);
int leftRev = findRevisionIndexToRevert(dirHistoryModel, label);
if (leftRev < 0) {
LocalHistoryLog.LOG.warn(
String.format("Couldn't find label revision. try to Use local history dialog for %s and perform revert manually.", f.getName()));
return;
}
try {
dirHistoryModel.selectRevisions(-1, leftRev - 1); //-1 because we should revert all changes up to previous one, but not label-related.
dirHistoryModel.createReverter().revert();
}
catch (IOException e) {
LocalHistoryLog.LOG.error(String.format("Couldn't revert %s to local history label.", f.getName()), e);
}
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2000-2016 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.history.integration;
import com.intellij.history.Label;
import com.intellij.history.core.revisions.ChangeRevision;
import com.intellij.history.core.revisions.Revision;
import com.intellij.history.integration.ui.models.HistoryDialogModel;
import com.intellij.history.integration.ui.models.RevisionItem;
import com.intellij.openapi.util.Condition;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class LocalHistoryUtil {
static int findRevisionIndexToRevert(@NotNull HistoryDialogModel dirHistoryModel, @NotNull Label label) {
List<RevisionItem> revs = dirHistoryModel.getRevisions();
for (int i = 0; i < revs.size(); i++) {
final RevisionItem rev = revs.get(i);
if (isLabelRevision(rev, label)) {
return i;
}
}
return -1;
}
static boolean isLabelRevision(@NotNull RevisionItem rev, @NotNull Label label) {
final long targetChangeId = label.getLabelChangeId();
return ContainerUtil.exists(rev.labels, new Condition<Revision>() {
@Override
public boolean value(Revision revision) {
return revision instanceof ChangeRevision && ((ChangeRevision)revision).containsChangeWithId(targetChangeId);
}
});
}
}
@@ -0,0 +1,110 @@
/*
* Copyright 2000-2016 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.history.integration.revertion;
import com.intellij.history.Label;
import com.intellij.history.LocalHistory;
import com.intellij.history.integration.IntegrationTestCase;
import com.intellij.openapi.vfs.VirtualFile;
public class HistoryReverterToLabelTest extends IntegrationTestCase {
public void testFileCreation() throws Exception {
createChildData(myRoot, "first.txt");
final LocalHistory localHistory = LocalHistory.getInstance();
final Label testLabel = localHistory.putSystemLabel(myProject, "testLabel");
createChildData(myRoot, "foo.txt");
localHistory.revertToLabel(myProject, myRoot, testLabel);
assertNull(myRoot.findChild("foo.txt"));
assertNotNull(myRoot.findChild("first.txt"));
}
public void testFileDeletion() throws Exception {
VirtualFile f = createChildData(myRoot, "foo.txt");
setBinaryContent(f, new byte[]{123}, -1, 4000, this);
final LocalHistory localHistory = LocalHistory.getInstance();
final Label testLabel = localHistory.putSystemLabel(myProject, "testLabel");
delete(f);
localHistory.revertToLabel(myProject, myRoot, testLabel);
f = myRoot.findChild("foo.txt");
assertNotNull(f);
assertEquals(123, f.contentsToByteArray()[0]);
assertEquals(4000, f.getTimeStamp());
}
public void testFileDeletionWithContent() throws Exception {
VirtualFile f = createChildData(myRoot, "foo.txt");
final LocalHistory localHistory = LocalHistory.getInstance();
final Label testLabel = localHistory.putSystemLabel(myProject, "testLabel");
setBinaryContent(f, new byte[]{123}, -1, 4000, this);
delete(f);
localHistory.revertToLabel(myProject, myRoot, testLabel);
f = myRoot.findChild("foo.txt");
assertNotNull(f);
assertEquals(0, f.contentsToByteArray().length);
}
public void testParentAndChildRename() throws Exception {
VirtualFile dir = createChildDirectory(myRoot, "dir");
VirtualFile f = createChildData(dir, "foo.txt");
int modificationStamp = -1;
setBinaryContent(f, new byte[]{123}, modificationStamp, 4000, this);
final LocalHistory localHistory = LocalHistory.getInstance();
final Label testLabel1 = localHistory.putSystemLabel(myProject, "testLabel");
rename(dir, "dir2");
final Label testLabel2 = localHistory.putSystemLabel(myProject, "testLabel");
rename(f, "bar.txt");
localHistory.revertToLabel(myProject, myRoot, testLabel2);
assertNotNull(myRoot.findChild("dir2"));
dir = myRoot.findChild("dir2");
assert dir != null;
assertNull(dir.findChild("bar.txt"));
f = dir.findChild("foo.txt");
assertNotNull(f);
assertEquals(123, f.contentsToByteArray()[0]);
assertEquals(4000, f.getTimeStamp());
localHistory.revertToLabel(myProject, myRoot, testLabel1);
assertNull(myRoot.findChild("dir2"));
dir = myRoot.findChild("dir");
assert dir != null;
assertNull(dir.findChild("bar.txt"));
f = dir.findChild("foo.txt");
assertNotNull(f);
assertEquals(123, f.contentsToByteArray()[0]);
assertEquals(4000, f.getTimeStamp());
}
public void testRevertContentChange() throws Exception {
VirtualFile f = createChildData(myRoot, "foo.txt");
int modificationStamp1 = -1;
setBinaryContent(f, new byte[]{1}, modificationStamp1, 1000, this);
final LocalHistory localHistory = LocalHistory.getInstance();
final Label testLabel = localHistory.putSystemLabel(myProject, "testLabel");
int modificationStamp = -1;
setBinaryContent(f, new byte[]{2}, modificationStamp, 2000, this);
setBinaryContent(f, new byte[]{3}, modificationStamp, 3000, this);
localHistory.revertToLabel(myProject, myRoot, testLabel);
f = myRoot.findChild("foo.txt");
assertNotNull(f);
assertEquals(1, f.contentsToByteArray()[0]);
assertEquals(1000, f.getTimeStamp());
}
}
@@ -16,6 +16,7 @@
package com.intellij.openapi.diff.impl.patch.formove;
import com.intellij.history.Label;
import com.intellij.history.LocalHistory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.text.StringUtil;
@@ -70,6 +71,7 @@ class UndoApplyPatchDialog extends DialogWrapper {
@Override
protected void doOKAction() {
super.doOKAction();
LocalHistory.getInstance().revertToLabel(myProject, myProject.getBaseDir(), myBeforeLabel);
}
static void rollbackApplyPatch(@NotNull Project project, @NotNull List<FilePath> filePaths,