summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Sohn <matthias.sohn@sap.com>2012-02-16 00:44:36 +0100
committerMatthias Sohn <matthias.sohn@sap.com>2012-02-16 00:44:47 +0100
commit42b0fb467916f17ea62c147e2a7387b193d765fd (patch)
tree8d9f33665b53c3d21317fc0b9db5e09dfe3eab09
parentde48250da5a424ffbf26db0a4b8bf0d83f39f76e (diff)
parentf70b24a60176c3bd35c1f67e9ea039078ea9a5ad (diff)
downloadjgit-42b0fb467916f17ea62c147e2a7387b193d765fd.tar.gz
jgit-42b0fb467916f17ea62c147e2a7387b193d765fd.zip
Merge branch 'stable-1.3'
* stable-1.3: Prepare post 1.3.0.201202151440-r build JGit 1.3.0.201202151440-r Generate conflicts and index updates on file mode changes Change-Id: Ie99780ef5cdea7b3ea1ea076282fe0a25f14f469 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java203
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java41
2 files changed, 235 insertions, 9 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index fafd745b5a..efb686da59 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -55,22 +55,27 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.api.CheckoutCommand;
+import org.eclipse.jgit.api.CheckoutResult;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeResult.MergeStatus;
import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.dircache.DirCacheEditor;
-import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.CheckoutConflictException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.FS;
import org.junit.Test;
public class DirCacheCheckoutTest extends RepositoryTestCase {
@@ -939,6 +944,202 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
}
}
+ @Test
+ public void testFileModeChangeWithNoContentChangeUpdate() throws Exception {
+ if (!FS.DETECTED.supportsExecute())
+ return;
+
+ Git git = Git.wrap(db);
+
+ // Add non-executable file
+ File file = writeTrashFile("file.txt", "a");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file));
+
+ // Create branch
+ git.branchCreate().setName("b1").call();
+
+ // Make file executable
+ db.getFS().setExecute(file, true);
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit2").call();
+
+ // Verify executable and working directory is clean
+ Status status = git.status().call();
+ assertTrue(status.getModified().isEmpty());
+ assertTrue(status.getChanged().isEmpty());
+ assertTrue(db.getFS().canExecute(file));
+
+ // Switch branches
+ git.checkout().setName("b1").call();
+
+ // Verify not executable and working directory is clean
+ status = git.status().call();
+ assertTrue(status.getModified().isEmpty());
+ assertTrue(status.getChanged().isEmpty());
+ assertFalse(db.getFS().canExecute(file));
+ }
+
+ @Test
+ public void testFileModeChangeAndContentChangeConflict() throws Exception {
+ if (!FS.DETECTED.supportsExecute())
+ return;
+
+ Git git = Git.wrap(db);
+
+ // Add non-executable file
+ File file = writeTrashFile("file.txt", "a");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file));
+
+ // Create branch
+ git.branchCreate().setName("b1").call();
+
+ // Make file executable
+ db.getFS().setExecute(file, true);
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit2").call();
+
+ // Verify executable and working directory is clean
+ Status status = git.status().call();
+ assertTrue(status.getModified().isEmpty());
+ assertTrue(status.getChanged().isEmpty());
+ assertTrue(db.getFS().canExecute(file));
+
+ writeTrashFile("file.txt", "b");
+
+ // Switch branches
+ CheckoutCommand checkout = git.checkout().setName("b1");
+ try {
+ checkout.call();
+ fail("Checkout exception not thrown");
+ } catch (JGitInternalException e) {
+ CheckoutResult result = checkout.getResult();
+ assertNotNull(result);
+ assertNotNull(result.getConflictList());
+ assertEquals(1, result.getConflictList().size());
+ assertTrue(result.getConflictList().contains("file.txt"));
+ }
+ }
+
+ @Test
+ public void testDirtyFileModeEqualHeadMerge()
+ throws Exception {
+ if (!FS.DETECTED.supportsExecute())
+ return;
+
+ Git git = Git.wrap(db);
+
+ // Add non-executable file
+ File file = writeTrashFile("file.txt", "a");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file));
+
+ // Create branch
+ git.branchCreate().setName("b1").call();
+
+ // Create second commit and don't touch file
+ writeTrashFile("file2.txt", "");
+ git.add().addFilepattern("file2.txt").call();
+ git.commit().setMessage("commit2").call();
+
+ // stage a mode change
+ writeTrashFile("file.txt", "a");
+ db.getFS().setExecute(file, true);
+ git.add().addFilepattern("file.txt").call();
+
+ // dirty the file
+ writeTrashFile("file.txt", "b");
+
+ assertEquals(
+ "[file.txt, mode:100755, content:a][file2.txt, mode:100644, content:]",
+ indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "b", "file2.txt", ""));
+
+ // Switch branches and check that the dirty file survived in worktree
+ // and index
+ git.checkout().setName("b1").call();
+ assertEquals("[file.txt, mode:100755, content:a]", indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "b"));
+ }
+
+ @Test
+ public void testDirtyFileModeEqualIndexMerge()
+ throws Exception {
+ if (!FS.DETECTED.supportsExecute())
+ return;
+
+ Git git = Git.wrap(db);
+
+ // Add non-executable file
+ File file = writeTrashFile("file.txt", "a");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file));
+
+ // Create branch
+ git.branchCreate().setName("b1").call();
+
+ // Create second commit with executable file
+ file = writeTrashFile("file.txt", "b");
+ db.getFS().setExecute(file, true);
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("commit2").call();
+
+ // stage the same content as in the branch we want to switch to
+ writeTrashFile("file.txt", "a");
+ db.getFS().setExecute(file, false);
+ git.add().addFilepattern("file.txt").call();
+
+ // dirty the file
+ writeTrashFile("file.txt", "c");
+ db.getFS().setExecute(file, true);
+
+ assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "c"));
+
+ // Switch branches and check that the dirty file survived in worktree
+ // and index
+ git.checkout().setName("b1").call();
+ assertEquals("[file.txt, mode:100644, content:a]", indexState(CONTENT));
+ assertWorkDir(mkmap("file.txt", "c"));
+ }
+
+ @Test
+ public void testFileModeChangeAndContentChangeNoConflict() throws Exception {
+ if (!FS.DETECTED.supportsExecute())
+ return;
+
+ Git git = Git.wrap(db);
+
+ // Add first file
+ File file1 = writeTrashFile("file1.txt", "a");
+ git.add().addFilepattern("file1.txt").call();
+ git.commit().setMessage("commit1").call();
+ assertFalse(db.getFS().canExecute(file1));
+
+ // Add second file
+ File file2 = writeTrashFile("file2.txt", "b");
+ git.add().addFilepattern("file2.txt").call();
+ git.commit().setMessage("commit2").call();
+ assertFalse(db.getFS().canExecute(file2));
+
+ // Create branch from first commit
+ assertNotNull(git.checkout().setCreateBranch(true).setName("b1")
+ .setStartPoint(Constants.HEAD + "~1").call());
+
+ // Change content and file mode in working directory and index
+ file1 = writeTrashFile("file1.txt", "c");
+ db.getFS().setExecute(file1, true);
+ git.add().addFilepattern("file1.txt").call();
+
+ // Switch back to 'master'
+ assertNotNull(git.checkout().setName(Constants.MASTER).call());
+ }
+
public void assertWorkDir(HashMap<String, String> i) throws CorruptObjectException,
IOException {
TreeWalk walk = new TreeWalk(db);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 9b397e88c9..58b691228d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -473,6 +473,23 @@ public class DirCacheCheckout {
}
/**
+ * Compares whether two pairs of ObjectId and FileMode are equal.
+ *
+ * @param id1
+ * @param mode1
+ * @param id2
+ * @param mode2
+ * @return <code>true</code> if FileModes and ObjectIds are equal.
+ * <code>false</code> otherwise
+ */
+ private boolean equalIdAndMode(ObjectId id1, FileMode mode1, ObjectId id2,
+ FileMode mode2) {
+ if (!mode1.equals(mode2))
+ return false;
+ return id1 != null ? id1.equals(id2) : id2 == null;
+ }
+
+ /**
* Here the main work is done. This method is called for each existing path
* in head, index and merge. This method decides what to do with the
* corresponding index entry: keep it, update it, remove it or mark a
@@ -508,6 +525,9 @@ public class DirCacheCheckout {
ObjectId iId = (i == null ? null : i.getEntryObjectId());
ObjectId mId = (m == null ? null : m.getEntryObjectId());
ObjectId hId = (h == null ? null : h.getEntryObjectId());
+ FileMode iMode = (i == null ? null : i.getEntryFileMode());
+ FileMode mMode = (m == null ? null : m.getEntryFileMode());
+ FileMode hMode = (h == null ? null : h.getEntryFileMode());
/**
* <pre>
@@ -596,7 +616,7 @@ public class DirCacheCheckout {
case 0xFDD: // 10 11
// TODO: make use of tree extension as soon as available in jgit
// we would like to do something like
- // if (!iId.equals(mId))
+ // if (!equalIdAndMode(iId, iMode, mId, mMode)
// conflict(name, i.getDirCacheEntry(), h, m);
// But since we don't know the id of a tree in the index we do
// nothing here and wait that conflicts between index and merge
@@ -610,7 +630,7 @@ public class DirCacheCheckout {
conflict(name, (i != null) ? i.getDirCacheEntry() : null, h, m);
break;
case 0xFDF: // 7 8 9
- if (hId.equals(mId)) {
+ if (equalIdAndMode(hId, hMode, mId, mMode)) {
if (isModified(name))
conflict(name, i.getDirCacheEntry(), h, m); // 8
else
@@ -625,7 +645,7 @@ public class DirCacheCheckout {
keep(i.getDirCacheEntry());
break;
case 0xFFD: // 12 13 14
- if (hId.equals(iId)) {
+ if (equalIdAndMode(hId, hMode, iId, iMode)) {
dce = i.getDirCacheEntry();
if (f == null || f.isModified(dce, true))
conflict(name, dce, h, m);
@@ -662,7 +682,9 @@ public class DirCacheCheckout {
if (!FileMode.GITLINK.equals(m.getEntryFileMode())) {
// a dirty worktree: the index is empty but we have a
// workingtree-file
- if (mId == null || !mId.equals(f.getEntryObjectId())) {
+ if (mId == null
+ || !equalIdAndMode(mId, mMode,
+ f.getEntryObjectId(), f.getEntryFileMode())) {
conflict(name, null, h, m);
return;
}
@@ -703,7 +725,7 @@ public class DirCacheCheckout {
* </pre>
*/
- if (m == null || mId.equals(iId)) {
+ if (m == null || equalIdAndMode(mId, mMode, iId, iMode)) {
if (m==null && walk.isDirectoryFileConflict()) {
if (dce != null
&& (f == null || f.isModified(dce, true)))
@@ -732,7 +754,7 @@ public class DirCacheCheckout {
// be removed from the index, but not deleted from disk.
remove(name);
} else {
- if (hId.equals(iId)) {
+ if (equalIdAndMode(hId, hMode, iId, iMode)) {
if (f == null || f.isModified(dce, true))
conflict(name, dce, h, m);
else
@@ -741,9 +763,12 @@ public class DirCacheCheckout {
conflict(name, dce, h, m);
}
} else {
- if (!hId.equals(mId) && !hId.equals(iId) && !mId.equals(iId))
+ if (!equalIdAndMode(hId, hMode, mId, mMode)
+ && !equalIdAndMode(hId, hMode, iId, iMode)
+ && !equalIdAndMode(mId, mMode, iId, iMode))
conflict(name, dce, h, m);
- else if (hId.equals(iId) && !mId.equals(iId)) {
+ else if (equalIdAndMode(hId, hMode, iId, iMode)
+ && !equalIdAndMode(mId, mMode, iId, iMode)) {
// For submodules just update the index with the new SHA-1
if (dce != null
&& FileMode.GITLINK.equals(dce.getFileMode())) {