private IList<FieldDesynchronization> GetFieldSyncStatus(SitecoreSourceItem item, ISerializedItem serializedItem, IFieldPredicate fieldPredicate)
        {
            var desyncs = new List<FieldDesynchronization>();

            var serializedVersion = serializedItem.Versions.FirstOrDefault(x => x.VersionNumber == item.InnerItem.Version.Number && x.Language == item.InnerItem.Language.Name);

            if (serializedVersion == null)
            {
                desyncs.Add(new FieldDesynchronization("Version"));
                return desyncs;
            }

            item.InnerItem.Fields.ReadAll();

            foreach (Field field in item.InnerItem.Fields)
            {
                if (field.ID == FieldIDs.Revision ||
                    field.ID == FieldIDs.Updated ||
                    field.ID == FieldIDs.Created ||
                    field.ID == FieldIDs.CreatedBy ||
                    field.ID == FieldIDs.UpdatedBy ||
                    field.Type.Equals("attachment", StringComparison.OrdinalIgnoreCase) ||
                    !fieldPredicate.Includes(field.ID).IsIncluded) continue;
                // we're doing a data comparison here - revision, created (by), updated (by) don't matter
                // skipping these fields allows us to ignore spurious saves the template builder makes to unchanged items being conflicts

                // find the field in the serialized item in either versioned or shared fields
                string serializedField;

                if(!serializedVersion.Fields.TryGetValue(field.ID.ToString(), out serializedField))
                    serializedItem.SharedFields.TryGetValue(field.ID.ToString(), out serializedField);

                // we ignore if the field doesn't exist in the serialized item. This is because if you added a field to a template,
                // that does not immediately re-serialize all items based on that template so it's likely innocuous - we're not overwriting anything.
                if (serializedField == null) continue;

                if (!serializedField.Equals(field.Value, StringComparison.Ordinal))
                {
                    desyncs.Add(new FieldDesynchronization(field.Name));
                }
            }

            return desyncs;
        }
        private string GetErrorValue(SaveArgs args)
        {
            var results = new Dictionary<Item, IList<FieldDesynchronization>>();

            try
            {
                foreach (var item in args.Items)
                {
                    Item existingItem = Client.ContentDatabase.GetItem(item.ID, item.Language, item.Version);

                    Assert.IsNotNull(existingItem, "Existing item {0} did not exist! This should never occur.", item.ID);

                    var existingSitecoreItem = new SitecoreSourceItem(existingItem);

                    foreach (var configuration in _configurations)
                    {
                        // ignore conflicts on items that Unicorn is not managing
                        if (!configuration.Resolve<IPredicate>().Includes(existingSitecoreItem).IsIncluded) continue;

                        ISerializedReference serializedReference = configuration.Resolve<ISerializationProvider>().GetReference(existingSitecoreItem);

                        if(serializedReference == null) continue;

                        // not having an existing serialized version means no possibility of conflict here
                        ISerializedItem serializedItem = serializedReference.GetItem();

                        if (serializedItem == null) continue;

                        var fieldPredicate = configuration.Resolve<IFieldPredicate>();

                        var fieldIssues = GetFieldSyncStatus(existingSitecoreItem, serializedItem, fieldPredicate);

                        if (fieldIssues.Count == 0) continue;

                        results.Add(existingItem, fieldIssues);
                    }

                }

                // no problems
                if (results.Count == 0) return null;

                var sb = new StringBuilder();
                sb.Append("CRITICAL MESSAGE FROM UNICORN:\n");
                sb.Append("You need to run a Unicorn sync. The following fields did not match the serialized version:\n");

                foreach (var item in results)
                {
                    if(results.Count > 1)
                        sb.AppendFormat("\n{0}: {1}", item.Key.DisplayName, string.Join(", ", item.Value.Select(x => x.FieldName)));
                    else
                        sb.AppendFormat("\n{0}", string.Join(", ", item.Value.Select(x => x.FieldName)));
                }

                sb.Append("\n\nDo you want to overwrite anyway?\nTHIS MAY CAUSE LOST WORK.");

                return sb.ToString();
            }
            catch (Exception ex)
            {
                Log.Error("Exception occurred while performing serialization conflict check!", ex, this);
                return "Exception occurred: " + ex.Message; // this will cause a few retries
            }
        }
        public void CopyItem(ItemDefinition source, ItemDefinition destination, string copyName, ID copyId, CallContext context)
        {
            if (DisableSerialization) return;

            // copying is easy - all we have to do is serialize the copyID. Copied children will all result in multiple calls to CopyItem so we don't even need to worry about them.
            var copiedItem = new SitecoreSourceItem(Database.GetItem(copyId));

            if (!_predicate.Includes(copiedItem).IsIncluded) return; // destination parent is not in a path that we are serializing, so skip out

            _serializationProvider.SerializeItem(copiedItem);
            _logger.CopiedItem(_serializationProvider.LogName, () => GetSourceFromDefinition(source), copiedItem);
        }