| Index: sync/engine/syncer_util.cc
|
| diff --git a/sync/engine/syncer_util.cc b/sync/engine/syncer_util.cc
|
| index 7f63b37ddbdeeeb0a654b54a12ef6a597b67e34a..d97f34cc17039bcef680b6606ed185f905d95220 100644
|
| --- a/sync/engine/syncer_util.cc
|
| +++ b/sync/engine/syncer_util.cc
|
| @@ -184,7 +184,23 @@ syncable::Id FindLocalIdToUpdate(
|
|
|
| return local_entry.GetId();
|
| }
|
| + } else if (update.has_server_defined_unique_tag() &&
|
| + !update.server_defined_unique_tag().empty()) {
|
| + // The client creates type root folders on demand with a local ID on
|
| + // demand when a progress marker for the given type is initially set.
|
| + // The server might also attempt to send a type root folder for the same
|
| + // type. It will does that during the transition period until support for
|
| + // root folders is removed on the server for most types. After that the
|
| + // server will still be creating type root folders for BOOKMARKS and
|
| + // NIGORI types.
|
| + syncable::Entry local_entry(trans, syncable::GET_BY_SERVER_TAG,
|
| + update.server_defined_unique_tag());
|
| + if (local_entry.good() && !local_entry.GetId().ServerKnows()) {
|
| + DCHECK(local_entry.GetId() != update_id);
|
| + return local_entry.GetId();
|
| + }
|
| }
|
| +
|
| // Fallback: target an entry having the server ID, creating one if needed.
|
| return update_id;
|
| }
|
| @@ -227,16 +243,24 @@ UpdateAttemptResponse AttemptToUpdateEntry(
|
|
|
| if (!entry->GetServerIsDel()) {
|
| syncable::Id new_parent = entry->GetServerParentId();
|
| - Entry parent(trans, GET_BY_ID, new_parent);
|
| - // A note on non-directory parents:
|
| - // We catch most unfixable tree invariant errors at update receipt time,
|
| - // however we deal with this case here because we may receive the child
|
| - // first then the illegal parent. Instead of dealing with it twice in
|
| - // different ways we deal with it once here to reduce the amount of code and
|
| - // potential errors.
|
| - if (!parent.good() || parent.GetIsDel() || !parent.GetIsDir()) {
|
| - DVLOG(1) << "Entry has bad parent, returning conflict_hierarchy.";
|
| - return CONFLICT_HIERARCHY;
|
| + if (!new_parent.IsNull()) {
|
| + // Perform this step only if the parent is specified.
|
| + // The unset parent means that the implicit type root would be used.
|
| + Entry parent(trans, GET_BY_ID, new_parent);
|
| + // A note on non-directory parents:
|
| + // We catch most unfixable tree invariant errors at update receipt time,
|
| + // however we deal with this case here because we may receive the child
|
| + // first then the illegal parent. Instead of dealing with it twice in
|
| + // different ways we deal with it once here to reduce the amount of code
|
| + // and
|
| + // potential errors.
|
| + if (!parent.good() || parent.GetIsDel() || !parent.GetIsDir()) {
|
| + DVLOG(1) << "Entry has bad parent, returning conflict_hierarchy.";
|
| + return CONFLICT_HIERARCHY;
|
| + }
|
| + } else {
|
| + DCHECK(
|
| + !IsTypeWithServerGeneratedRoot(GetModelTypeFromSpecifics(specifics)));
|
| }
|
| if (entry->GetParentId() != new_parent) {
|
| if (!entry->GetIsDel() && !IsLegalNewParent(trans, id, new_parent)) {
|
|
|