Result (Map<String, Long> oldChecksums, Map<String, Long> newChecksums, Set<String> critical, Set<String> optional, boolean lookForMoved) { Result result = new Result(); result.commonFiles = collect(oldChecksums, newChecksums, critical, true); result.filesToDelete = withAllRemoved(oldChecksums, newChecksums); Map<String, Long> toUpdate = collect(oldChecksums, newChecksums, critical, false); Map<String, Long> toCreate = withAllRemoved(newChecksums, oldChecksums); result.filesToCreate = lookForMoved ? new LinkedHashMap<>() : toCreate; result.filesToUpdate = new LinkedHashMap<>(); for (Map.Entry<String, Long> update : toUpdate.entrySet()) { if (Digester.isSymlink(update.getValue()) || Digester.isSymlink(newChecksums.get(update.getKey()))) { result.filesToDelete.put(update.getKey(), update.getValue()); result.filesToCreate.put(update.getKey(), Digester.INVALID); } else { result.filesToUpdate.put(update.getKey(), new Update(update.getKey(), update.getValue(), false)); } } if (lookForMoved) { Map<Long, List<String>> byContent = groupFilesByContent(result.filesToDelete); Map<String, List<String>> byName = groupFilesByName(result.filesToDelete); for (Map.Entry<String, Long> create : toCreate.entrySet()) { if (Digester.isFile(create.getValue())) { List<String> sameContent = byContent.get(create.getValue()); String source = findBestCandidateForMove(sameContent, create.getKey(), optional); boolean move = true; if (source == null) { List<String> sameName = byName.get(new File(create.getKey()).getName()); source = findBestCandidateForMove(sameName, create.getKey(), optional); move = false; } if (source != null && !critical.contains(source)) { result.filesToUpdate.put(create.getKey(), new Update(source, result.filesToDelete.get(source), move)); continue; } } result.filesToCreate.put(create.getKey(), create.getValue()); } } return result; }