in org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/merge/GitMergeEditorInput.java [335:586]
private DiffContainer buildDiffContainer(Repository repository,
RevCommit headCommit, RevCommit ancestorCommit, RevWalk rw,
IProgressMonitor monitor)
throws IOException, InterruptedException {
monitor.setTaskName(UIText.GitMergeEditorInput_CalculatingDiffTaskName);
DiffContainer result = new DiffNode(Differencer.CONFLICTING);
ConflictStyle style = null;
try (TreeWalk tw = new TreeWalk(repository)) {
int dirCacheIndex = tw.addTree(new DirCacheIterator(repository
.readDirCache()));
FileTreeIterator fIter = new FileTreeIterator(repository);
int fileTreeIndex = tw.addTree(fIter);
fIter.setDirCacheIterator(tw, dirCacheIndex);
int repositoryTreeIndex = tw.addTree(rw.parseTree(repository
.resolve(Constants.HEAD)));
// filter by selected resources
Collection<String> filterPaths = getFilterPaths();
if (!filterPaths.isEmpty()) {
if (filterPaths.size() > 1) {
tw.setFilter(
PathFilterGroup.createFromStrings(filterPaths));
} else {
String path = filterPaths.iterator().next();
if (!path.isEmpty()) {
tw.setFilter(PathFilterGroup.createFromStrings(path));
}
}
}
tw.setRecursive(true);
while (tw.next()) {
if (monitor.isCanceled())
throw new InterruptedException();
String gitPath = tw.getPathString();
monitor.setTaskName(gitPath);
FileTreeIterator fit = tw.getTree(fileTreeIndex,
FileTreeIterator.class);
if (fit == null)
continue;
DirCacheIterator dit = tw.getTree(dirCacheIndex,
DirCacheIterator.class);
final DirCacheEntry dirCacheEntry = dit == null ? null : dit
.getDirCacheEntry();
boolean conflicting = dirCacheEntry != null
&& dirCacheEntry.getStage() > 0;
AbstractTreeIterator rt = tw.getTree(repositoryTreeIndex,
AbstractTreeIterator.class);
// compare local file against HEAD to see if it was modified
boolean modified = rt != null
&& !fit.getEntryObjectId()
.equals(rt.getEntryObjectId());
// if this is neither conflicting nor changed, we skip it
if (!conflicting && !modified)
continue;
ITypedElement right;
String encoding = null;
if (conflicting) {
GitFileRevision revision = GitFileRevision.inIndex(
repository, gitPath, DirCacheEntry.STAGE_3);
encoding = CompareCoreUtils.getResourceEncoding(repository,
gitPath);
right = new FileRevisionTypedElement(revision, encoding);
} else {
right = CompareUtils.getFileRevisionTypedElement(gitPath,
headCommit, repository);
}
// can this really happen?
if (right instanceof EmptyTypedElement) {
continue;
}
ITypedElement left;
IFileRevision rev;
// if the file is not conflicting (as it was auto-merged)
// we will show the auto-merged (local) version
Path repositoryPath = new Path(repository.getWorkTree()
.getAbsolutePath());
IPath location = repositoryPath.append(gitPath);
assert location != null;
IFile file = ResourceUtil.getFileForLocation(location, false);
boolean useWorkingTree = !conflicting || useWorkspace;
boolean stage2FromWorkingTree = false;
if (!useWorkingTree && conflicting && dirCacheEntry != null) {
// Normal conflict stages have a zero timestamp. If it's not
// zero, we marked it below when the content was saved to
// the working tree file in an earlier merge editor.
useWorkingTree = !Instant.EPOCH
.equals(dirCacheEntry.getLastModifiedInstant());
stage2FromWorkingTree = useWorkingTree;
}
if (useWorkingTree) {
boolean useOursFilter = conflicting && useOurs;
int conflictMarkerSize = 7; // Git default
if (useOursFilter) {
Attributes attributes = tw.getAttributes();
useOursFilter = attributes.canBeContentMerged();
if (useOursFilter) {
Attribute markerSize = attributes
.get("conflict-marker-size"); //$NON-NLS-1$
if (markerSize != null && Attribute.State.CUSTOM
.equals(markerSize.getState())) {
try {
conflictMarkerSize = Integer
.parseUnsignedInt(
markerSize.getValue());
} catch (NumberFormatException e) {
// Ignore
}
}
}
}
LocalResourceTypedElement item;
if (useOursFilter) {
if (style == null) {
style = repository.getConfig().getEnum(
ConfigConstants.CONFIG_MERGE_SECTION, null,
ConfigConstants.CONFIG_KEY_CONFLICTSTYLE,
ConflictStyle.MERGE);
}
boolean useDiff3Style = ConflictStyle.DIFF3
.equals(style);
String filter = (useDiff3Style ? 'O' : 'o')
+ Integer.toString(conflictMarkerSize);
URI uri = EgitFileSystem.createURI(repository, gitPath,
"WORKTREE:" + filter); //$NON-NLS-1$
Charset rscEncoding = null;
if (file != null) {
if (encoding == null) {
encoding = CompareCoreUtils
.getResourceEncoding(file);
}
try {
rscEncoding = Charset.forName(encoding);
} catch (IllegalArgumentException e) {
// Ignore here; use default.
}
}
item = createWithHiddenResource(uri, repository,
gitPath, tw.getNameString(), file, rscEncoding);
if (file != null) {
item.setSharedDocumentListener(
new LocalResourceSaver(item) {
@Override
protected void save()
throws CoreException {
super.save();
file.refreshLocal(
IResource.DEPTH_ZERO, null);
}
});
} else {
item.setSharedDocumentListener(
new LocalResourceSaver(item));
}
} else {
if (file != null) {
item = new LocalResourceTypedElement(file);
} else {
item = new LocalNonWorkspaceTypedElement(repository,
location);
}
item.setSharedDocumentListener(
new LocalResourceSaver(item));
}
left = item;
} else {
IFile rsc = file != null ? file
: createHiddenResource(location.toFile().toURI(),
tw.getNameString(), null);
assert rsc != null;
// Stage 2 from index with backing IResource
rev = GitFileRevision.inIndex(repository, gitPath,
DirCacheEntry.STAGE_2);
IRunnableContext runnableContext = getContainer();
if (runnableContext == null) {
runnableContext = PlatformUI.getWorkbench()
.getProgressService();
assert runnableContext != null;
}
left = new ResourceEditableRevision(rev, rsc,
runnableContext);
// 'left' saves to the working tree. Update the index entry
// with the current time. Normal conflict stages have a
// timestamp of zero, so this is a non-invasive fully
// compatible way to mark this conflict stage so that the
// next time we do take the file contents.
((EditableRevision) left).addContentChangeListener(
source -> updateIndexTimestamp(repository,
gitPath));
// make sure we don't need a round trip later
try {
((EditableRevision) left).cacheContents(monitor);
} catch (CoreException e) {
throw new IOException(e.getMessage(), e);
}
}
int kind = Differencer.NO_CHANGE;
if (conflicting) {
kind = Differencer.CONFLICTING + Differencer.CHANGE;
} else if (modified) {
kind = Differencer.LEFT + Differencer.ADDITION;
}
IDiffContainer fileParent = getFileParent(result,
repositoryPath, file, location);
ITypedElement ancestor = null;
if (ancestorCommit != null) {
ancestor = CompareUtils.getFileRevisionTypedElement(gitPath,
ancestorCommit, repository);
// we get an ugly black icon if we have an EmptyTypedElement
// instead of null
if (ancestor instanceof EmptyTypedElement) {
ancestor = null;
}
} else if (conflicting) {
GitFileRevision revision = GitFileRevision.inIndex(
repository, gitPath, DirCacheEntry.STAGE_1);
if (encoding == null) {
encoding = CompareCoreUtils
.getResourceEncoding(repository, gitPath);
}
ancestor = new FileRevisionTypedElement(revision, encoding);
}
// create the node as child
DiffNode node = new MergeDiffNode(fileParent, kind, ancestor,
left, right);
if (stage2FromWorkingTree) {
customLabels.put(node, tw.getNameString());
} else if (left instanceof EditableRevision) {
String name = tw.getNameString();
((EditableRevision) left).addContentChangeListener(
source -> setLeftLabel(node, name, true));
}
}
return result;
} catch (URISyntaxException e) {
throw new IOException(e.getMessage(), e);
}
}