in org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java [710:1172]
void processEntry(CanonicalTreeParser h, CanonicalTreeParser m,
DirCacheBuildIterator i, WorkingTreeIterator f) throws IOException {
DirCacheEntry dce = i != null ? i.getDirCacheEntry() : null;
String name = walk.getPathString();
if (m != null)
checkValidPath(m);
if (i == null && m == null && h == null) {
// File/Directory conflict case #20
if (walk.isDirectoryFileConflict())
// TODO: check whether it is always correct to report a conflict here
conflict(name, null, null, null);
// file only exists in working tree -> ignore it
return;
}
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>
* File/Directory conflicts:
* the following table from ReadTreeTest tells what to do in case of directory/file
* conflicts. I give comments here
*
* H I M Clean H==M H==I I==M Result
* ------------------------------------------------------------------
* 1 D D F Y N Y N Update
* 2 D D F N N Y N Conflict
* 3 D F D Y N N Keep
* 4 D F D N N N Conflict
* 5 D F F Y N N Y Keep
* 5b D F F Y N N N Conflict
* 6 D F F N N N Y Keep
* 6b D F F N N N N Conflict
* 7 F D F Y Y N N Update
* 8 F D F N Y N N Conflict
* 9 F D F N N N Conflict
* 10 F D D N N Y Keep
* 11 F D D N N N Conflict
* 12 F F D Y N Y N Update
* 13 F F D N N Y N Conflict
* 14 F F D N N N Conflict
* 15 0 F D N N N Conflict
* 16 0 D F Y N N N Update
* 17 0 D F N N N Conflict
* 18 F 0 D Update
* 19 D 0 F Update
* 20 0 0 F N (worktree=dir) Conflict
* </pre>
*/
// The information whether head,index,merge iterators are currently
// pointing to file/folder/non-existing is encoded into this variable.
//
// To decode write down ffMask in hexadecimal form. The last digit
// represents the state for the merge iterator, the second last the
// state for the index iterator and the third last represents the state
// for the head iterator. The hexadecimal constant "F" stands for
// "file", a "D" stands for "directory" (tree), and a "0" stands for
// non-existing. Symbolic links and git links are treated as File here.
//
// Examples:
// ffMask == 0xFFD -> Head=File, Index=File, Merge=Tree
// ffMask == 0xDD0 -> Head=Tree, Index=Tree, Merge=Non-Existing
int ffMask = 0;
if (h != null)
ffMask = FileMode.TREE.equals(hMode) ? 0xD00 : 0xF00;
if (i != null)
ffMask |= FileMode.TREE.equals(iMode) ? 0x0D0 : 0x0F0;
if (m != null)
ffMask |= FileMode.TREE.equals(mMode) ? 0x00D : 0x00F;
// Check whether we have a possible file/folder conflict. Therefore we
// need a least one file and one folder.
if (((ffMask & 0x222) != 0x000)
&& (((ffMask & 0x00F) == 0x00D) || ((ffMask & 0x0F0) == 0x0D0) || ((ffMask & 0xF00) == 0xD00))) {
// There are 3*3*3=27 possible combinations of file/folder
// conflicts. Some of them are not-relevant because
// they represent no conflict, e.g. 0xFFF, 0xDDD, ... The following
// switch processes all relevant cases.
switch (ffMask) {
case 0xDDF: // 1 2
if (f != null && isModifiedSubtree_IndexWorkingtree(name)) {
conflict(name, dce, h, m); // 1
} else {
update(name, mId, mMode); // 2
}
break;
case 0xDFD: // 3 4
keep(name, dce, f);
break;
case 0xF0D: // 18
remove(name);
break;
case 0xDFF: // 5 5b 6 6b
if (equalIdAndMode(iId, iMode, mId, mMode))
keep(name, dce, f); // 5 6
else
conflict(name, dce, h, m); // 5b 6b
break;
case 0xFDD: // 10 11
// TODO: make use of tree extension as soon as available in jgit
// we would like to do something like
// 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
// are found later
break;
case 0xD0F: // 19
update(name, mId, mMode);
break;
case 0xDF0: // conflict without a rule
case 0x0FD: // 15
conflict(name, dce, h, m);
break;
case 0xFDF: // 7 8 9
if (equalIdAndMode(hId, hMode, mId, mMode)) {
if (isModifiedSubtree_IndexWorkingtree(name))
conflict(name, dce, h, m); // 8
else
update(name, mId, mMode); // 7
} else
conflict(name, dce, h, m); // 9
break;
case 0xFD0: // keep without a rule
keep(name, dce, f);
break;
case 0xFFD: // 12 13 14
if (equalIdAndMode(hId, hMode, iId, iMode))
if (f != null
&& f.isModified(dce, true,
this.walk.getObjectReader()))
conflict(name, dce, h, m); // 13
else
remove(name); // 12
else
conflict(name, dce, h, m); // 14
break;
case 0x0DF: // 16 17
if (!isModifiedSubtree_IndexWorkingtree(name))
update(name, mId, mMode);
else
conflict(name, dce, h, m);
break;
default:
keep(name, dce, f);
}
return;
}
if ((ffMask & 0x222) == 0) {
// HEAD, MERGE and index don't contain a file (e.g. all contain a
// folder)
if (f == null || FileMode.TREE.equals(f.getEntryFileMode())) {
// the workingtree entry doesn't exist or also contains a folder
// -> no problem
return;
}
// the workingtree entry exists and is not a folder
if (!idEqual(h, m)) {
// Because HEAD and MERGE differ we will try to update the
// workingtree with a folder -> return a conflict
conflict(name, null, null, null);
}
return;
}
if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
// File/Directory conflict case #20
conflict(name, null, h, m);
return;
}
if (i == null) {
// Nothing in Index
// At least one of Head, Index, Merge is not empty
// make sure not to overwrite untracked files
if (f != null && !f.isEntryIgnored()) {
// A submodule is not a file. We should ignore it
if (!FileMode.GITLINK.equals(mMode)) {
// a dirty worktree: the index is empty but we have a
// workingtree-file
if (mId == null
|| !equalIdAndMode(mId, mMode,
f.getEntryObjectId(), f.getEntryFileMode())) {
conflict(name, null, h, m);
return;
}
}
}
/**
* <pre>
* I (index) H M H==M Result
* -------------------------------------------
* 0 nothing nothing nothing (does not happen)
* 1 nothing nothing exists use M
* 2 nothing exists nothing remove path from index
* 3 nothing exists exists yes keep index if not in initial checkout
* , otherwise use M
* nothing exists exists no fail
* </pre>
*/
if (h == null)
// Nothing in Head
// Nothing in Index
// At least one of Head, Index, Merge is not empty
// -> only Merge contains something for this path. Use it!
// Potentially update the file
update(name, mId, mMode); // 1
else if (m == null)
// Nothing in Merge
// Something in Head
// Nothing in Index
// -> only Head contains something for this path and it should
// be deleted. Potentially removes the file!
remove(name); // 2
else { // 3
// Something in Merge
// Something in Head
// Nothing in Index
// -> Head and Merge contain something (maybe not the same) and
// in the index there is nothing (e.g. 'git rm ...' was
// called before). Ignore the cached deletion and use what we
// find in Merge. Potentially updates the file.
if (equalIdAndMode(hId, hMode, mId, mMode)) {
if (initialCheckout || force) {
update(name, mId, mMode);
} else {
keep(name, dce, f);
}
} else {
conflict(name, dce, h, m);
}
}
} else {
// Something in Index
if (h == null) {
// Nothing in Head
// Something in Index
/**
* <pre>
* clean I==H I==M H M Result
* -----------------------------------------------------
* 4 yes N/A N/A nothing nothing keep index
* 5 no N/A N/A nothing nothing keep index
*
* 6 yes N/A yes nothing exists keep index
* 7 no N/A yes nothing exists keep index
* 8 yes N/A no nothing exists fail
* 9 no N/A no nothing exists fail
* </pre>
*/
if (m == null
|| !isModified_IndexTree(name, iId, iMode, mId, mMode,
mergeCommitTree)) {
// Merge contains nothing or the same as Index
// Nothing in Head
// Something in Index
if (m==null && walk.isDirectoryFileConflict()) {
// Nothing in Merge and current path is part of
// File/Folder conflict
// Nothing in Head
// Something in Index
if (dce != null
&& (f == null || f.isModified(dce, true,
this.walk.getObjectReader())))
// No file or file is dirty
// Nothing in Merge and current path is part of
// File/Folder conflict
// Nothing in Head
// Something in Index
// -> File folder conflict and Merge wants this
// path to be removed. Since the file is dirty
// report a conflict
conflict(name, dce, h, m);
else
// A file is present and file is not dirty
// Nothing in Merge and current path is part of
// File/Folder conflict
// Nothing in Head
// Something in Index
// -> File folder conflict and Merge wants this path
// to be removed. Since the file is not dirty remove
// file and index entry
remove(name);
} else
// Something in Merge or current path is not part of
// File/Folder conflict
// Merge contains nothing or the same as Index
// Nothing in Head
// Something in Index
// -> Merge contains nothing new. Keep the index.
keep(name, dce, f);
} else
// Merge contains something and it is not the same as Index
// Nothing in Head
// Something in Index
// -> Index contains something new (different from Head)
// and Merge is different from Index. Report a conflict
conflict(name, dce, h, m);
} else if (m == null) {
// Nothing in Merge
// Something in Head
// Something in Index
/**
* <pre>
* clean I==H I==M H M Result
* -----------------------------------------------------
* 10 yes yes N/A exists nothing remove path from index
* 11 no yes N/A exists nothing keep file
* 12 yes no N/A exists nothing fail
* 13 no no N/A exists nothing fail
* </pre>
*/
if (iMode == FileMode.GITLINK) {
// A submodule in Index
// Nothing in Merge
// Something in Head
// Submodules that disappear from the checkout must
// be removed from the index, but not deleted from disk.
remove(name);
} else {
// Something different from a submodule in Index
// Nothing in Merge
// Something in Head
if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
headCommitTree)) {
// Index contains the same as Head
// Something different from a submodule in Index
// Nothing in Merge
// Something in Head
if (f != null
&& f.isModified(dce, true,
this.walk.getObjectReader())) {
// file is dirty
// Index contains the same as Head
// Something different from a submodule in Index
// Nothing in Merge
// Something in Head
if (!FileMode.TREE.equals(f.getEntryFileMode())
&& FileMode.TREE.equals(iMode)) {
// The workingtree contains a file and the index semantically contains a folder.
// Git considers the workingtree file as untracked. Just keep the untracked file.
return;
}
// -> file is dirty and tracked but is should be
// removed. That's a conflict
conflict(name, dce, h, m);
} else {
// file doesn't exist or is clean
// Index contains the same as Head
// Something different from a submodule in Index
// Nothing in Merge
// Something in Head
// -> Remove from index and delete the file
remove(name);
}
} else {
// Index contains something different from Head
// Something different from a submodule in Index
// Nothing in Merge
// Something in Head
// -> Something new is in index (and maybe even on the
// filesystem). But Merge wants the path to be removed.
// Report a conflict
conflict(name, dce, h, m);
}
}
} else {
// Something in Merge
// Something in Head
// Something in Index
if (!equalIdAndMode(hId, hMode, mId, mMode)
&& isModified_IndexTree(name, iId, iMode, hId, hMode,
headCommitTree)
&& isModified_IndexTree(name, iId, iMode, mId, mMode,
mergeCommitTree))
// All three contents in Head, Merge, Index differ from each
// other
// -> All contents differ. Report a conflict.
conflict(name, dce, h, m);
else
// At least two of the contents of Head, Index, Merge
// are the same
// Something in Merge
// Something in Head
// Something in Index
if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
headCommitTree)
&& isModified_IndexTree(name, iId, iMode, mId, mMode,
mergeCommitTree)) {
// Head contains the same as Index. Merge differs
// Something in Merge
// For submodules just update the index with the new SHA-1
if (dce != null
&& FileMode.GITLINK.equals(dce.getFileMode())) {
// Index and Head contain the same submodule. Merge
// differs
// Something in Merge
// -> Nothing new in index. Move to merge.
// Potentially updates the file
// TODO check that we don't overwrite some unsaved
// file content
update(name, mId, mMode);
} else if (dce != null
&& (f != null && f.isModified(dce, true,
this.walk.getObjectReader()))) {
// File exists and is dirty
// Head and Index don't contain a submodule
// Head contains the same as Index. Merge differs
// Something in Merge
// -> Merge wants the index and file to be updated
// but the file is dirty. Report a conflict
conflict(name, dce, h, m);
} else {
// File doesn't exist or is clean
// Head and Index don't contain a submodule
// Head contains the same as Index. Merge differs
// Something in Merge
// -> Standard case when switching between branches:
// Nothing new in index but something different in
// Merge. Update index and file
update(name, mId, mMode);
}
} else {
// Head differs from index or merge is same as index
// At least two of the contents of Head, Index, Merge
// are the same
// Something in Merge
// Something in Head
// Something in Index
// Can be formulated as: Either all three states are
// equal or Merge is equal to Head or Index and differs
// to the other one.
// -> In all three cases we don't touch index and file.
keep(name, dce, f);
}
}
}
}