Browse Source

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>
tags/v2.0.0.201206130900-r
Matthias Sohn 12 years ago
parent
commit
42b0fb4679

+ 202
- 1
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java View File

@@ -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);

+ 33
- 8
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java View File

@@ -472,6 +472,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
@@ -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())) {

Loading…
Cancel
Save