Add isModeDifferent method to WorkingTreeIterator that compares mode with consideration of the core.filemode setting in the config. Bug: 379004 Change-Id: I07335300d787a69c3d1608242238991d5b5214ac Signed-off-by: Christian Halstrick <christian.halstrick@sap.com>tags/v2.0.0.201206130900-r
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | import static org.junit.Assert.assertFalse; | ||||
import static org.junit.Assert.assertNotNull; | |||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import java.io.File; | import java.io.File; | ||||
import org.eclipse.jgit.api.ResetCommand.ResetType; | import org.eclipse.jgit.api.ResetCommand.ResetType; | ||||
import org.eclipse.jgit.api.errors.GitAPIException; | import org.eclipse.jgit.api.errors.GitAPIException; | ||||
import org.eclipse.jgit.api.errors.JGitInternalException; | import org.eclipse.jgit.api.errors.JGitInternalException; | ||||
import org.eclipse.jgit.dircache.DirCache; | |||||
import org.eclipse.jgit.lib.ConfigConstants; | |||||
import org.eclipse.jgit.lib.Constants; | import org.eclipse.jgit.lib.Constants; | ||||
import org.eclipse.jgit.lib.FileMode; | |||||
import org.eclipse.jgit.lib.RepositoryState; | import org.eclipse.jgit.lib.RepositoryState; | ||||
import org.eclipse.jgit.lib.RepositoryTestCase; | import org.eclipse.jgit.lib.RepositoryTestCase; | ||||
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; | import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; | ||||
.exists()); | .exists()); | ||||
} | } | ||||
@Test | |||||
public void testCherryPickOverExecutableChangeOnNonExectuableFileSystem() | |||||
throws Exception { | |||||
Git git = new Git(db); | |||||
File file = writeTrashFile("test.txt", "a"); | |||||
assertNotNull(git.add().addFilepattern("test.txt").call()); | |||||
assertNotNull(git.commit().setMessage("commit1").call()); | |||||
assertNotNull(git.checkout().setCreateBranch(true).setName("a").call()); | |||||
writeTrashFile("test.txt", "b"); | |||||
assertNotNull(git.add().addFilepattern("test.txt").call()); | |||||
RevCommit commit2 = git.commit().setMessage("commit2").call(); | |||||
assertNotNull(commit2); | |||||
assertNotNull(git.checkout().setName(Constants.MASTER).call()); | |||||
DirCache cache = db.lockDirCache(); | |||||
cache.getEntry("test.txt").setFileMode(FileMode.EXECUTABLE_FILE); | |||||
cache.write(); | |||||
assertTrue(cache.commit()); | |||||
cache.unlock(); | |||||
assertNotNull(git.commit().setMessage("commit3").call()); | |||||
db.getFS().setExecute(file, false); | |||||
git.getRepository() | |||||
.getConfig() | |||||
.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, | |||||
ConfigConstants.CONFIG_KEY_FILEMODE, false); | |||||
CherryPickResult result = git.cherryPick().include(commit2).call(); | |||||
assertNotNull(result); | |||||
assertEquals(CherryPickStatus.OK, result.getStatus()); | |||||
} | |||||
private RevCommit prepareCherryPick(final Git git) throws Exception { | private RevCommit prepareCherryPick(final Git git) throws Exception { | ||||
// create, add and commit file a | // create, add and commit file a | ||||
writeTrashFile("a", "a"); | writeTrashFile("a", "a"); |
else { | else { | ||||
// the preferred version THEIRS has a different mode | // the preferred version THEIRS has a different mode | ||||
// than ours. Check it out! | // than ours. Check it out! | ||||
if (isWorktreeDirty()) | |||||
if (isWorktreeDirty(work)) | |||||
return false; | return false; | ||||
DirCacheEntry e = add(tw.getRawPath(), theirs, | DirCacheEntry e = add(tw.getRawPath(), theirs, | ||||
DirCacheEntry.STAGE_0); | DirCacheEntry.STAGE_0); | ||||
// THEIRS. THEIRS is chosen. | // THEIRS. THEIRS is chosen. | ||||
// Check worktree before checking out THEIRS | // Check worktree before checking out THEIRS | ||||
if (isWorktreeDirty()) | |||||
if (isWorktreeDirty(work)) | |||||
return false; | return false; | ||||
if (nonTree(modeT)) { | if (nonTree(modeT)) { | ||||
DirCacheEntry e = add(tw.getRawPath(), theirs, | DirCacheEntry e = add(tw.getRawPath(), theirs, | ||||
if (nonTree(modeO) && nonTree(modeT)) { | if (nonTree(modeO) && nonTree(modeT)) { | ||||
// Check worktree before modifying files | // Check worktree before modifying files | ||||
if (isWorktreeDirty()) | |||||
if (isWorktreeDirty(work)) | |||||
return false; | return false; | ||||
MergeResult<RawText> result = contentMerge(base, ours, theirs); | MergeResult<RawText> result = contentMerge(base, ours, theirs); | ||||
// OURS was deleted checkout THEIRS | // OURS was deleted checkout THEIRS | ||||
if (modeO == 0) { | if (modeO == 0) { | ||||
// Check worktree before checking out THEIRS | // Check worktree before checking out THEIRS | ||||
if (isWorktreeDirty()) | |||||
if (isWorktreeDirty(work)) | |||||
return false; | return false; | ||||
if (nonTree(modeT)) { | if (nonTree(modeT)) { | ||||
if (e != null) | if (e != null) | ||||
return isDirty; | return isDirty; | ||||
} | } | ||||
private boolean isWorktreeDirty() { | |||||
private boolean isWorktreeDirty(WorkingTreeIterator work) { | |||||
if (inCore) | if (inCore) | ||||
return false; | return false; | ||||
final int modeO = tw.getRawMode(T_OURS); | final int modeO = tw.getRawMode(T_OURS); | ||||
// Worktree entry has to match ours to be considered clean | // Worktree entry has to match ours to be considered clean | ||||
final boolean isDirty = nonTree(modeF) | |||||
&& !(modeO == modeF && tw.idEqual(T_FILE, T_OURS)); | |||||
final boolean isDirty; | |||||
if (nonTree(modeF)) | |||||
isDirty = work.isModeDifferent(modeO) | |||||
|| !tw.idEqual(T_FILE, T_OURS); | |||||
else | |||||
isDirty = false; | |||||
if (isDirty) | if (isDirty) | ||||
failingPaths.put(tw.getPathString(), | failingPaths.put(tw.getPathString(), | ||||
MergeFailureReason.DIRTY_WORKTREE); | MergeFailureReason.DIRTY_WORKTREE); |
DIFFER_BY_TIMESTAMP | DIFFER_BY_TIMESTAMP | ||||
} | } | ||||
/** | |||||
* Is the file mode of the current entry different than the given raw mode? | |||||
* | |||||
* @param rawMode | |||||
* @return true if different, false otherwise | |||||
*/ | |||||
public boolean isModeDifferent(final int rawMode) { | |||||
// Determine difference in mode-bits of file and index-entry. In the | |||||
// bitwise presentation of modeDiff we'll have a '1' when the two modes | |||||
// differ at this position. | |||||
int modeDiff = getEntryRawMode() ^ rawMode; | |||||
if (modeDiff == 0) | |||||
return false; | |||||
// Do not rely on filemode differences in case of symbolic links | |||||
if (FileMode.SYMLINK.equals(rawMode)) | |||||
return false; | |||||
// Ignore the executable file bits if WorkingTreeOptions tell me to | |||||
// do so. Ignoring is done by setting the bits representing a | |||||
// EXECUTABLE_FILE to '0' in modeDiff | |||||
if (!state.options.isFileMode()) | |||||
modeDiff &= ~FileMode.EXECUTABLE_FILE.getBits(); | |||||
return modeDiff != 0; | |||||
} | |||||
/** | /** | ||||
* Compare the metadata (mode, length, modification-timestamp) of the | * Compare the metadata (mode, length, modification-timestamp) of the | ||||
* current entry and a {@link DirCacheEntry} | * current entry and a {@link DirCacheEntry} | ||||
if (!entry.isSmudged() && entry.getLength() != (int) getEntryLength()) | if (!entry.isSmudged() && entry.getLength() != (int) getEntryLength()) | ||||
return MetadataDiff.DIFFER_BY_METADATA; | return MetadataDiff.DIFFER_BY_METADATA; | ||||
// Determine difference in mode-bits of file and index-entry. In the | |||||
// bitwise presentation of modeDiff we'll have a '1' when the two modes | |||||
// differ at this position. | |||||
int modeDiff = getEntryRawMode() ^ entry.getRawMode(); | |||||
// Do not rely on filemode differences in case of symbolic links | |||||
if (modeDiff != 0 && !FileMode.SYMLINK.equals(entry.getRawMode())) { | |||||
// Ignore the executable file bits if WorkingTreeOptions tell me to | |||||
// do so. Ignoring is done by setting the bits representing a | |||||
// EXECUTABLE_FILE to '0' in modeDiff | |||||
if (!state.options.isFileMode()) | |||||
modeDiff &= ~FileMode.EXECUTABLE_FILE.getBits(); | |||||
if (modeDiff != 0) | |||||
// Report a modification if the modes still (after potentially | |||||
// ignoring EXECUTABLE_FILE bits) differ | |||||
return MetadataDiff.DIFFER_BY_METADATA; | |||||
} | |||||
if (isModeDifferent(entry.getRawMode())) | |||||
return MetadataDiff.DIFFER_BY_METADATA; | |||||
// Git under windows only stores seconds so we round the timestamp | // Git under windows only stores seconds so we round the timestamp | ||||
// Java gives us if it looks like the timestamp in index is seconds | // Java gives us if it looks like the timestamp in index is seconds |