in git-server/src/main/java/jetbrains/buildServer/buildTriggers/vcs/git/gitProxy/GitProxyChangesCollector.java [460:614]
public void logAnyDifferences(@NotNull List<ModificationData> jgitData, @NotNull List<ModificationData> gitProxyData,
@NotNull RepositoryStateData fromState, @NotNull RepositoryStateData toState,
@NotNull VcsRoot root, @NotNull GitApiClient<GitRepoApi> gitRepoApi,
@NotNull Map<String, List<String>> submodulePrefixes) {
long startTime = System.currentTimeMillis();
if (jgitData.size() != gitProxyData.size()) {
if (jgitData.size() == 0) {
if (fromState.getBranchRevisions().size() == 1) {
String defaultBranchRevision = fromState.getDefaultBranchRevision();
if (defaultBranchRevision != null && !doesCommitStillExist(defaultBranchRevision, gitRepoApi)) {
// in this case jgit returns empty result because there is no exisiting branch revisions in FromState
return;
}
}
logDiffLength(jgitData, gitProxyData, fromState, toState, root, " Jgit result is empty.");
return;
}
Map<String, ModificationData> gitProxyDatMap = new HashMap<>(gitProxyData.size());
Map<String, ModificationData> jgitDataMap = new HashMap<>(jgitData.size());
Map<String, String> jgitReverseEdges = new HashMap<>(jgitData.size());
for (ModificationData data: gitProxyData) {
gitProxyDatMap.put(data.getVersion(), data);
}
Map<String, ExtraDataState> jgitExtraData = new HashMap<>();
for (ModificationData data: jgitData) {
jgitDataMap.put(data.getVersion(), data);
if (!gitProxyDatMap.containsKey(data.getVersion())) {
jgitExtraData.put(data.getVersion(), ExtraDataState.UNKNOWN);
}
for (String parentVersion : data.getParentRevisions()) {
jgitReverseEdges.put(parentVersion, data.getVersion());
}
}
for (ModificationData data: gitProxyData) {
if (!jgitDataMap.containsKey(data.getVersion())) {
logDiffLength(jgitData, gitProxyData, fromState, toState, root, " gitProxy result has extra commits.");
return;
}
}
for (String bottomModVersion: jgitExtraData.keySet()) {
if (System.currentTimeMillis() - startTime > 10000) {
throw new RuntimeException("Compare diff timeout");
}
ModificationData data = jgitDataMap.get(bottomModVersion);
if (data.getParentRevisions().stream().anyMatch(parent -> jgitDataMap.containsKey(parent))) {
// this is not the bottom modification in the returned result
continue;
}
// try to find the top modification from which this modification is reachable
String parentVersion = data.getVersion();
List<ModificationData> branchModifications = new ArrayList<>();
// this var is to avoid visiting the same commits twice
boolean forcePushed = false;
while (parentVersion != null) {
if (System.currentTimeMillis() - startTime > 10000) {
throw new RuntimeException("Compare diff timeout");
}
if (jgitExtraData.containsKey(parentVersion) && jgitExtraData.get(parentVersion) == ExtraDataState.FORCE_PUSHED) {
forcePushed = true;
break;
}
branchModifications.add(jgitDataMap.get(parentVersion));
parentVersion = jgitReverseEdges.get(parentVersion);
}
if (!forcePushed) {
ModificationData topModification = branchModifications.get(branchModifications.size() - 1);
if (!gitProxyDatMap.containsKey(topModification.getVersion())) {
// this is likely the case when the branch was rolled back, it is expected to have empty result from git proxy for the branch. There are 2 cases
// check if from commit still exists, this is the case when the branch was rolled back and the changes were already collected for the old HEAD
// if it doesn't, then the gitProxy returned correct empty result for the branch
String correspondingFromRev = findCorrespondingFromRevision(topModification.getVersion(), fromState, toState, jgitDataMap,false);
ModificationData bottomModData = jgitDataMap.get(bottomModVersion);
if (correspondingFromRev != null && /*checking that it wasn't some old commit*/!bottomModData.getParentRevisions().contains(correspondingFromRev)
&& !doesCommitStillExist(correspondingFromRev, gitRepoApi)) {
continue;
}
// check if top commit still exists, if it doesn't this means that the git proxy returned correct empty result. This is the case when the branch was rolled back
// and the changes were not collected for the old HEAD. Otherwise, we should report the difference
if (doesCommitStillExist(topModification.getVersion(), gitRepoApi)) {
logDiffLength(jgitData, gitProxyData, fromState, toState, root,
String.format(" Version %s still exists, but gitProxy didn't return it.", topModification.getVersion()));
return;
}
} else {
// we need to find the corresponding from version for this branch to check if jgit returned more commits because of force push
String correspondingFromRev = findCorrespondingFromRevision(topModification.getVersion(), fromState, toState, jgitDataMap, true);
if (correspondingFromRev == null) {
logDiffLength(jgitData, gitProxyData, fromState, toState, root,
String.format(" From state revision wasn't found for to state revision %s.", topModification.getVersion()));
return;
}
if (doesCommitStillExist(correspondingFromRev, gitRepoApi)) {
logDiffLength(jgitData, gitProxyData, fromState, toState, root, String.format(" From state revision %s still exists.", correspondingFromRev));
return;
}
}
}
// mark commits as force pushed so we won't try to process them again
branchModifications.forEach(mod -> {
if (jgitExtraData.containsKey(mod.getVersion())) {
jgitExtraData.put(mod.getVersion(), ExtraDataState.FORCE_PUSHED);
}
});
}
return;
}
if (TeamCityProperties.getBoolean(COMPARE_MODIFICATIONS_WITHOUT_ORDER_LOGGING_PROPERTY) && notEqualModificationListsById(jgitData, gitProxyData)) {
jgitData = new ArrayList<>(jgitData);
gitProxyData = new ArrayList<>(gitProxyData);
Collections.sort(jgitData, (a, b) -> a.getVersion().compareTo(b.getVersion()));
Collections.sort(gitProxyData, (a, b) -> a.getVersion().compareTo(b.getVersion()));
}
List<ModificationData> diff = new ArrayList<>();
List<Integer> differentPostions = new ArrayList<>();
for (int i = 0; i < jgitData.size(); i++) {
ModDataComparisonResult cmpRes = compareModifications(jgitData.get(i), gitProxyData.get(i), submodulePrefixes.get(jgitData.get(i).getVersion()));
if (cmpRes == ModDataComparisonResult.NOT_EQUAL_VCS_CHANGES) {
diff.add(jgitData.get(i));
diff.add(gitProxyData.get(i));
differentPostions.add(i);
}
else if (cmpRes == ModDataComparisonResult.NOT_EQUAL_ATTRIBUTES) {
diff.add(getDataWithoutFileChanges(jgitData.get(i), false, true, gitProxyData.get(i), false));
diff.add(getDataWithoutFileChanges(gitProxyData.get(i), false, true, jgitData.get(i), true));
differentPostions.add(i);
}
else if (cmpRes == ModDataComparisonResult.NOT_EQUAL_OTHER) {
diff.add(getDataWithoutFileChanges(jgitData.get(i), false, false));
diff.add(getDataWithoutFileChanges(gitProxyData.get(i), false, false));
differentPostions.add(i);
}
if (diff.size() > TeamCityProperties.getInteger("teamcity.git.gitProxy.changesCollectionComparison.maxDiffSize", 100)) break; // too many different commits, it does not make sense to compute the rest
}
if (!differentPostions.isEmpty()) {
LOG.info(
"GitProxy difference was found for VCS root(" + LogUtil.describe(root) + "). Different ModificationData at positions " + myGson.toJson(differentPostions)
+ ". diff:{" + myGson.toJson(diff)
+ "}, "
+ getStateDiff(fromState, toState) +
", equal:{" + myGson.toJson(getCommitOnlyModificationDataList(jgitData)) + "}"
);
}
}