static BuildProperties CreateBuildProperties( IRestApi restApi, string taskNumber, string branchName, string labelName, string buildStagePreCiOrPostCi, string destinationBranch, MultilinerBotConfiguration botConfig) { int branchHeadChangesetId = MultilinerMergebotApi.GetBranchHead( restApi, botConfig.Repository, branchName); ChangesetModel branchHeadChangeset = MultilinerMergebotApi.GetChangeset( restApi, botConfig.Repository, branchHeadChangesetId); int trunkHeadChangesetId = MultilinerMergebotApi.GetBranchHead( restApi, botConfig.Repository, destinationBranch); ChangesetModel trunkHeadChangeset = MultilinerMergebotApi.GetChangeset( restApi, botConfig.Repository, trunkHeadChangesetId); return(new BuildProperties { BranchName = branchName, TaskNumber = taskNumber, BranchHead = branchHeadChangeset.ChangesetId.ToString(), BranchHeadGuid = branchHeadChangeset.Guid.ToString(), ChangesetOwner = branchHeadChangeset.Owner, TrunkBranchName = destinationBranch, TrunkHead = trunkHeadChangeset.ChangesetId.ToString(), TrunkHeadGuid = trunkHeadChangeset.Guid.ToString(), RepSpec = string.Format("{0}@{1}", botConfig.Repository, botConfig.Server), LabelName = labelName, Stage = buildStagePreCiOrPostCi }); }
internal static ShelveResult TryMergeToShelves( IRestApi restApi, Branch branch, string[] destinationBranches, MergeReport mergeReport, string taskTitle, string botName) { ShelveResult result = new ShelveResult(); foreach (string destinationBranch in destinationBranches) { MergeToResponse mergeToResult = MultilinerMergebotApi.MergeBranchTo( restApi, branch.Repository, branch.FullName, destinationBranch, GetComment(branch.FullName, destinationBranch, taskTitle, botName), MultilinerMergebotApi.MergeToOptions.CreateShelve); result.MergeStatusByTargetBranch[destinationBranch] = mergeToResult.Status; if (mergeToResult.Status == MergeToResultStatus.MergeNotNeeded) { string warningMessage = string.Format( "Branch [{0}] was already merged to [{1}] (No merge needed).", branch.FullName, destinationBranch); result.MergesNotNeededMessages.Add(warningMessage); continue; } if (IsFailedMergeTo(mergeToResult)) { string errorMsg = string.Format( "Can't merge branch [{0}] to [{1}]. Reason: {2}.", branch.FullName, destinationBranch, mergeToResult.Message); result.ErrorMessages.Add(errorMsg); BuildMergeReport.AddFailedMergeProperty( mergeReport, mergeToResult.Status, mergeToResult.Message); continue; } result.ShelvesByTargetBranch[destinationBranch] = mergeToResult.ChangesetNumber; BuildMergeReport.AddSucceededMergeProperty(mergeReport, mergeToResult.Status); } return(result); }
static string[] GetMergeToDestinationBranches( IRestApi restApi, Branch branch, string mergeToBranchesAttrName) { string rawAttrValue = string.Empty; try { rawAttrValue = MultilinerMergebotApi.GetBranchAttribute( restApi, branch.Repository, branch.FullName, mergeToBranchesAttrName); } catch (Exception e) { mLog.WarnFormat( "Unable to retrieve attribute [{0}] value from branch [{1}]. Error: {2}", mergeToBranchesAttrName, branch.FullName, e.Message); return(new string[] { }); } if (string.IsNullOrWhiteSpace(rawAttrValue)) { return new string[] { } } ; List <string> destinationBranchesList = new List <string>(); string[] rawSplittedDestinationBranches = rawAttrValue.Split( new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries); string cleanDstBranchName; foreach (string rawSplittedDestinationBranch in rawSplittedDestinationBranches) { cleanDstBranchName = rawSplittedDestinationBranch.Trim(); if (string.IsNullOrWhiteSpace(cleanDstBranchName)) { continue; } if (destinationBranchesList.Contains(cleanDstBranchName)) { continue; } destinationBranchesList.Add(cleanDstBranchName); } return(destinationBranchesList.ToArray()); }
static bool IsMergeAllowed(IRestApi restApi, Branch branch, string destinationBranch) { if (MultilinerMergebotApi.IsMergeAllowed( restApi, branch.Repository, branch.FullName, destinationBranch)) { return(true); } mLog.WarnFormat( "Branch [{0}] is not yet ready to be merged into [{1}] " + "Jumping to next branch in the queue...", branch.FullName, destinationBranch); return(false); }
internal static bool ExistsAttributeName( IRestApi restApi, string repository, string attributeName) { string query = string.Format("attributetype where name='{0}' ", attributeName); JArray findResult = MultilinerMergebotApi.Find( restApi, repository, query, DATE_FORMAT, "retrieve the list of attributes named " + attributeName, new string[] { "name" }); return(findResult != null && findResult.Count > 0); }
internal static void SafeDeleteShelves( IRestApi restApi, string repository, string[] destinationBranches, ShelveResult mergesToShelvesResult) { if (destinationBranches == null || destinationBranches.Length == 0 || mergesToShelvesResult == null || mergesToShelvesResult.ShelvesByTargetBranch == null) { return; } int shelveId = -1; foreach (string destinationBranch in destinationBranches) { if (!mergesToShelvesResult.ShelvesByTargetBranch.ContainsKey(destinationBranch)) { continue; } shelveId = mergesToShelvesResult.ShelvesByTargetBranch[destinationBranch]; if (shelveId == -1) { continue; } try { MultilinerMergebotApi.DeleteShelve(restApi, repository, shelveId); } catch (Exception ex) { mLog.ErrorFormat( "Unable to delete shelve {0} on repository '{1}': {2}", shelveId, repository, ex.Message); mLog.DebugFormat( "StackTrace:{0}{1}", Environment.NewLine, ex.StackTrace); } } }
internal static Result SetTaskAsFailed( IRestApi restApi, Branch branch, string taskNumber, MultilinerBotConfiguration botConfig, string codeReviewsStorageFile) { Result result = new Result(); result.IsSuccessful = true; try { if (botConfig.Plastic.IsApprovedCodeReviewFilterEnabled) { SetBranchReviewsAsPending( restApi, branch.Repository, branch.Id, codeReviewsStorageFile); } MultilinerMergebotApi.ChangeBranchAttribute( restApi, branch.Repository, branch.FullName, botConfig.Plastic.StatusAttribute.Name, botConfig.Plastic.StatusAttribute.FailedValue); if (taskNumber != null && botConfig.Issues != null) { MultilinerMergebotApi.Issues.SetIssueField( restApi, botConfig.Issues.Plug, botConfig.Issues.ProjectKey, taskNumber, botConfig.Issues.StatusField.Name, botConfig.Issues.StatusField.FailedValue); } } catch (Exception ex) { result.IsSuccessful = false; result.ErrorMessage = BuildExceptionErrorMsg( branch.FullName, botConfig.Plastic.StatusAttribute.FailedValue, ex.Message); } return(result); }
internal static string GetBranchName( IRestApi restApi, string repository, string branchId) { string query = string.Format("branch where id={0}", branchId); JArray findResult = MultilinerMergebotApi.Find( restApi, repository, query, DATE_FORMAT, "retrieve a single branch by ID", new string[] { "name" }); if (findResult.Count == 0) { return(string.Empty); } return(GetStringValue((JObject)findResult[0], "name")); }
static bool ExistsBranch(IRestApi restApi, string destinationBranch, string repository) { BranchModel destinationBranchModel = null; try { destinationBranchModel = MultilinerMergebotApi.GetBranch( restApi, repository, destinationBranch); return(!string.IsNullOrWhiteSpace(destinationBranchModel.Name)); } catch (Exception e) { mLog.WarnFormat( "Unable to locate branch name [{0}] in repository [{1}]. Error: {2}", destinationBranch, repository, e.Message); return(false); } }
internal static List <Branch> FindResolvedBranches( IRestApi restApi, string repository, string prefix, string statusAttributeName, string resolvedStatusAttributeValue) { string query = string.Format( "branch where ( name like '{0}%' or name like '{1}%' or name like '{2}%' ) " + "and date > '{3}' " + "and attribute='{4}' and ( attrvalue='{5}' or attrvalue='{6}' or attrvalue='{7}') ", prefix, prefix.ToLowerInvariant(), prefix.ToUpperInvariant(), DateTime.Now.AddYears(-1).ToString(DATE_FORMAT), statusAttributeName, resolvedStatusAttributeValue, resolvedStatusAttributeValue.ToLowerInvariant(), resolvedStatusAttributeValue.ToUpperInvariant()); JArray findResult = MultilinerMergebotApi.Find( restApi, repository, query, DATE_FORMAT, "retrieve the list of branches to process", new string[] { "id", "name", "owner", "comment" }); List <Branch> result = new List <Branch>(); foreach (JObject obj in findResult) { result.Add(new Branch( repository, GetStringValue(obj, "id"), GetStringValue(obj, "name"), GetStringValue(obj, "owner"), GetStringValue(obj, "comment"))); } return(result); }
internal static Result SetTaskAsTesting( IRestApi restApi, Branch branch, string taskNumber, MultilinerBotConfiguration botConfig) { Result result = new Result(); result.IsSuccessful = true; try { if (!string.IsNullOrEmpty(botConfig.Plastic.StatusAttribute.TestingValue)) { MultilinerMergebotApi.ChangeBranchAttribute( restApi, branch.Repository, branch.FullName, botConfig.Plastic.StatusAttribute.Name, botConfig.Plastic.StatusAttribute.TestingValue); } if (taskNumber != null && botConfig.Issues != null && !string.IsNullOrEmpty(botConfig.Issues.StatusField.TestingValue)) { MultilinerMergebotApi.Issues.SetIssueField( restApi, botConfig.Issues.Plug, botConfig.Issues.ProjectKey, taskNumber, botConfig.Issues.StatusField.Name, botConfig.Issues.StatusField.TestingValue); } } catch (Exception ex) { result.IsSuccessful = false; result.ErrorMessage = BuildExceptionErrorMsg( branch.FullName, botConfig.Plastic.StatusAttribute.TestingValue, ex.Message); } return(result); }
internal static List <BranchWithReview> FindPendingBranchesWithReviews( IRestApi restApi, string repository, string prefix, string statusAttributeName, string mergedStatusAttributeValue) { string reviewTypeConditionClause = string.Empty; //branches from a year ago matching with prefix with status!=merged (even those without any status set) string branchTypeConditionClause = string.Format( "( " + " name like '{0}%' or name like '{1}%' or name like '{2}%' " + ") " + "and " + "( " + " date > '{3}' " + ") " + "and " + "( " + " (not attribute='{4}') or " + " (attribute='{4}' and not ( attrvalue='{5}' or attrvalue='{6}' or attrvalue='{7}' )) " + ") ", prefix, prefix.ToUpperInvariant(), prefix.ToLowerInvariant(), DateTime.Now.AddYears(-1).ToString(DATE_FORMAT), statusAttributeName, mergedStatusAttributeValue, mergedStatusAttributeValue.ToUpperInvariant(), mergedStatusAttributeValue.ToLowerInvariant()); string[] outputFields = new string[] { "branchid", "branchname", "branchowner", "branchcomment", "reviewid", "reviewtargetid", "reviewstatus", "reviewtitle" }; JArray findResult = MultilinerMergebotApi.FindBranchesWithReviews( restApi, repository, reviewTypeConditionClause, branchTypeConditionClause, DATE_FORMAT, "retrieve the list of branches with reviews to process", outputFields); List <BranchWithReview> result = new List <BranchWithReview>(); Branch branch = null; Review review = null; foreach (JObject obj in findResult) { branch = new Branch( repository, GetStringValue(obj, "branchid"), GetStringValue(obj, "branchname"), GetStringValue(obj, "branchowner"), GetStringValue(obj, "branchcomment")); review = new Review( repository, GetStringValue(obj, "reviewid"), GetStringValue(obj, "reviewtargetid"), TranslateCodeReviewStatus(GetStringValue(obj, "reviewstatus")), GetStringValue(obj, "reviewtitle")); result.Add(new BranchWithReview() { Branch = branch, Review = review }); } return(result); }
public static Result TryProcessBranch( IRestApi restApi, Branch branch, MultilinerBotConfiguration botConfig, string botName, string codeReviewsStorageFile) { string taskNumber = null; MergeReport mergeReport = null; string[] destinationBranches = null; MergeToOperations.ShelveResult mergesToShelves = null; string notificationMsg = null; try { mLog.InfoFormat("Getting task number of branch {0} ...", branch.FullName); taskNumber = GetTaskNumber(branch.FullName, botConfig.BranchPrefix); if (!IsTaskReady( restApi, taskNumber, botConfig.Issues, botConfig.Plastic.IsApprovedCodeReviewFilterEnabled, branch.Repository, branch.Id, codeReviewsStorageFile)) { return(Result.NotReady); } destinationBranches = GetMergeToDestinationBranches( restApi, branch, botConfig.MergeToBranchesAttrName); if (destinationBranches == null || destinationBranches.Length == 0) { notificationMsg = string.Format( "The attribute [{0}] of branch [{1}@{2}@{3}] is not properly set. " + "Branch [{1}@{2}@{3}] status will be set as 'failed': [{4}].", botConfig.MergeToBranchesAttrName, branch.FullName, branch.Repository, botConfig.Server, botConfig.Plastic.StatusAttribute.FailedValue); mLog.Warn(notificationMsg); ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } foreach (string destinationBranch in destinationBranches) { if (ExistsBranch(restApi, destinationBranch, branch.Repository)) { continue; } notificationMsg = string.Format( "The destination branch [{0}@{1}@{2}] specified in attribute [{3}] " + "of branch [{4}@{1}@{2}] does not exist. " + "Branch [{4}@{1}@{2}] status will be set as 'failed': [{5}].", destinationBranch, branch.Repository, botConfig.Server, botConfig.MergeToBranchesAttrName, branch.FullName, botConfig.Plastic.StatusAttribute.FailedValue); mLog.Warn(notificationMsg); ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } foreach (string destinationBranch in destinationBranches) { if (IsMergeAllowed(restApi, branch, destinationBranch)) { continue; } return(Result.NotReady); } mLog.InfoFormat("Building the merge report of task {0} ...", taskNumber); mergeReport = BuildMergeReport.Build( MultilinerMergebotApi.GetBranch(restApi, branch.Repository, branch.FullName)); string taskTittle; string taskUrl; if (GetIssueInfo( restApi, taskNumber, botConfig.Issues, out taskTittle, out taskUrl)) { BuildMergeReport.AddIssueProperty(mergeReport, taskTittle, taskUrl); } mLog.InfoFormat("Trying to shelve server-side-merge from [{0}] to [{1}]", branch.FullName, string.Join(", ", destinationBranches)); mergesToShelves = MergeToOperations.TryMergeToShelves( restApi, branch, destinationBranches, mergeReport, taskTittle, botName); if (NoMergesNeeded(mergesToShelves)) { ChangeTaskStatus.Result chStatResult = ChangeTaskStatus.SetTaskAsMerged( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); notificationMsg = string.Join(Environment.NewLine, mergesToShelves.MergesNotNeededMessages); if (!string.IsNullOrWhiteSpace(chStatResult.ErrorMessage)) { notificationMsg = string.Concat( notificationMsg, Environment.NewLine, chStatResult.ErrorMessage); } Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } if (mergesToShelves.ErrorMessages.Count > 0) { ChangeTaskStatus.Result chStatResult = ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); notificationMsg = string.Join(Environment.NewLine, mergesToShelves.ErrorMessages); if (mergesToShelves.MergesNotNeededMessages.Count > 0) { notificationMsg = string.Concat( notificationMsg, Environment.NewLine, string.Join(Environment.NewLine, mergesToShelves.MergesNotNeededMessages)); } if (!string.IsNullOrWhiteSpace(chStatResult.ErrorMessage)) { notificationMsg = string.Concat( notificationMsg, Environment.NewLine, chStatResult.ErrorMessage); } Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } if (mergesToShelves.MergesNotNeededMessages.Count > 0) { string alreadyMergedMessage = string.Join( Environment.NewLine + "\t", mergesToShelves.MergesNotNeededMessages); notificationMsg = string.Format( "Branch [{0}] is already merged to some " + "of the specified destination branches in the attribute [{1}]. " + "The {2} mergebot will continue building the " + "merge(s) from branch [{0}] to [{3}].{4}{4}" + "Report of already merged branches:{4}\t{5}", branch.FullName, botConfig.MergeToBranchesAttrName, botName, string.Join(", ", mergesToShelves.ShelvesByTargetBranch.Keys), Environment.NewLine, alreadyMergedMessage); mLog.Info(notificationMsg); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); } bool allBuildsOk = Build.PreCheckinStage( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToShelves, botConfig, codeReviewsStorageFile); if (!allBuildsOk) { return(Result.Failed); } MergeToOperations.CheckinResult mergesToCheckins = MergeToOperations.TryApplyShelves( restApi, branch, destinationBranches, mergesToShelves, mergeReport, taskNumber, taskTittle, botName, botConfig, codeReviewsStorageFile); //checkin went OK in all target branches if (mergesToCheckins.ErrorMessages.Count == 0 && mergesToCheckins.DestinationNewChangesWarnings.Count == 0) { mLog.InfoFormat("Setting branch {0} as 'integrated'...", branch.FullName); ChangeTaskStatus.SetTaskAsMerged( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); notificationMsg = string.Format( "OK: Branch [{0}] was successfully merged to [{1}]", branch.FullName, string.Join(", ", mergesToShelves.ShelvesByTargetBranch.Keys)); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Build.PostCheckinStage( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToCheckins, botConfig, codeReviewsStorageFile)); } //some of the checkins went wrong -> we must run the post-ci plan for the successful ones, //but force-set as failed the source branch (or requeue if the errors are due to New DstChanges if (mergesToCheckins.ErrorMessages.Count > 0) { ChangeTaskStatus.Result chStatResult = ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); string checkinErrorMessage = string.Join( Environment.NewLine + "\t", mergesToCheckins.ErrorMessages); if (mergesToCheckins.DestinationNewChangesWarnings.Count > 0) { string dstChangesErrorMessage = string.Join( Environment.NewLine + "\t", mergesToCheckins.DestinationNewChangesWarnings); checkinErrorMessage = string.Concat( checkinErrorMessage, Environment.NewLine + "\t", dstChangesErrorMessage); } notificationMsg = string.Format( "Failed build. The result of building merges from branch [{0}] to [{1}] went OK, " + "but there were some errors checking-in the resulting shelves:{2}\t{3}{2}{2}{4}", branch.FullName, string.Join(", ", mergesToShelves.ShelvesByTargetBranch.Keys), Environment.NewLine, checkinErrorMessage, string.IsNullOrWhiteSpace(chStatResult.ErrorMessage) ? string.Empty : chStatResult.ErrorMessage); mLog.Warn(notificationMsg); if (mergesToCheckins.ChangesetsByTargetBranch.Count == 0) { Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } Build.PostCheckinStage( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToCheckins, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.Failed); } //LAST block -> if (mergesToCheckins.DestinationNewChangesWarnings.Count > 0) //Some of the checkins went wrong due to new changeset on at least a destination branch-> //We must run the post-ci plan for the successful ones, //but force requeuing of source branch, so the failed merges to dst branches are re-run mLog.InfoFormat("Setting branch {0} as 'resolved' (enqueue) ...", branch.FullName); MultilinerMergebotApi.ChangeBranchAttribute( restApi, branch.Repository, branch.FullName, botConfig.Plastic.StatusAttribute.Name, botConfig.Plastic.StatusAttribute.ResolvedValue); string dstBranchesNewCsetsMsg = string.Join( Environment.NewLine + "\t", mergesToCheckins.DestinationNewChangesWarnings); notificationMsg = string.Format( "Branch [{0}] will be enqueued again, as new changesets appeared in " + "merge destination branches, and thus, the branch needs to be tested again to " + "include those new changesets in the merge. Full report:{1}\t{2}", branch.FullName, Environment.NewLine, dstBranchesNewCsetsMsg); mLog.Warn(notificationMsg); if (mergesToCheckins.ChangesetsByTargetBranch.Count == 0) { Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.NotReady); } Build.PostCheckinStage( restApi, branch, mergeReport, taskNumber, destinationBranches, mergesToCheckins, botConfig, codeReviewsStorageFile); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); return(Result.NotReady); } catch (Exception ex) { mLog.ErrorFormat( "The attempt to process task {0} failed for branch {1}: {2}", taskNumber, branch.FullName, ex.Message); mLog.DebugFormat( "StackTrace:{0}{1}", Environment.NewLine, ex.StackTrace); ChangeTaskStatus.SetTaskAsFailed( restApi, branch, taskNumber, botConfig, codeReviewsStorageFile); notificationMsg = string.Format( "Can't process branch [{0}] because of an unexpected error: {1}.", branch.FullName, ex.Message); Notifier.NotifyTaskStatus( restApi, branch.Owner, notificationMsg, botConfig.Notifiers); BuildMergeReport.SetUnexpectedExceptionProperty(mergeReport, ex.Message); return(Result.Failed); } finally { ReportMerge(restApi, branch.Repository, branch.FullName, botName, mergeReport); MergeToOperations.SafeDeleteShelves( restApi, branch.Repository, destinationBranches, mergesToShelves); } }
internal static CheckinResult TryApplyShelves( IRestApi restApi, Branch branch, string[] destinationBranches, ShelveResult shelves, MergeReport mergeReport, string taskNumber, string taskTitle, string botName, MultilinerBotConfiguration botConfig, string codeReviewsStorageFile) { CheckinResult result = new CheckinResult(); int shelveId; foreach (string destinationBranch in destinationBranches) { if (!shelves.ShelvesByTargetBranch.ContainsKey(destinationBranch)) { continue; } shelveId = shelves.ShelvesByTargetBranch[destinationBranch]; mLog.InfoFormat( "Checking-in shelveset [{0}] from branch [{1}] to [{2}]", shelveId, branch.FullName, destinationBranch); MergeToResponse mergeResult = MultilinerMergebotApi.MergeShelveTo( restApi, branch.Repository, shelveId, destinationBranch, GetComment(branch.FullName, destinationBranch, taskTitle, botName), MultilinerMergebotApi.MergeToOptions.EnsureNoDstChanges); result.MergeStatusByTargetBranch[destinationBranch] = mergeResult.Status; BuildMergeReport.UpdateMergeProperty(mergeReport, mergeResult.Status, mergeResult.ChangesetNumber); if (mergeResult.Status == MergeToResultStatus.OK) { result.ChangesetsByTargetBranch[destinationBranch] = mergeResult.ChangesetNumber; mLog.InfoFormat( "Checkin: Created changeset [{0}] in branch [{1}]", mergeResult.ChangesetNumber, destinationBranch); continue; } if (mergeResult.Status == MergeToResultStatus.DestinationChanges) { string dstWarnMessage = string.Format( "Can't checkin shelve [{0}], the resulting shelve from merging branch " + "[{1}] to [{2}]. Reason: new changesets appeared in destination branch " + "while mergebot {3} was processing the merge from [{1}] to [{2}].{4}{5}", shelveId, branch.FullName, destinationBranch, botName, Environment.NewLine, mergeResult.Message); // it should checkin the shelve only on the exact parent shelve cset. // if there are new changes in the destination branch enqueue again the task result.DestinationNewChangesWarnings.Add(dstWarnMessage); continue; } string errorMsg = string.Format( "Can't checkin shelve [{0}], the resulting shelve from merging branch " + "[{1}] to [{2}]. Reason: {3}", shelveId, branch.FullName, destinationBranch, mergeResult.Message); result.ErrorMessages.Add(errorMsg); } return(result); }