void SG_dbrecord__add_pair__int64(SG_context* pCtx, SG_dbrecord* prec, const char* putf8Name, SG_int64 intValue) { SG_int_to_string_buffer buf; SG_int64_to_sz(intValue,buf); SG_ERR_CHECK_RETURN( SG_dbrecord__add_pair(pCtx, prec, putf8Name, buf) ); }
static void _tree__process_next_pending_item(SG_context * pCtx, _tree_t * pTree, SG_vhash * pMergeBaselines) { SG_uint32 i; _node_t * pNode = NULL; // The node we are processing. SG_uint32 iNode = 0; // Index of pNode in the 'pending' list. SG_uint32 countVcParents = 0; const char ** paszVcParentHids = NULL; SG_uint32 iVcParent; // The first pending node that needs to be processed is always the one with // the highest revno. Find it in the list. for(i=1; i < pTree->pending.count; ++i) { if(pTree->pending.p[i]->revno > pTree->pending.p[iNode]->revno) iNode = i; } pNode = pTree->pending.p[iNode]; // Load in the node's display children/vc parents. SG_ASSERT(pNode->displayChildren.count==0); SG_ERR_CHECK( SG_VHASH__ALLOC(pCtx, &pNode->pVcParents) ); SG_ERR_CHECK( SG_dagnode__get_parents__ref(pCtx, pNode->pDagnode, &countVcParents, &paszVcParentHids) ); for(iVcParent=0; iVcParent<countVcParents; ++iVcParent) { // Each vc parent is a candidate display child. const char * pszHidCandidate = paszVcParentHids[iVcParent]; _node_t * pNodeRef = NULL; // Scan through the list of 'pending' nodes to see if we have already // fetched this one... SG_uint32 iCandidate = pTree->pending.count; for(i=0; i < pTree->pending.count && iCandidate==pTree->pending.count; ++i) { if(strcmp(pTree->pending.p[i]->pszHidRef, pszHidCandidate)==0) { iCandidate = i; pNodeRef = pTree->pending.p[i]; } } if(iCandidate == pTree->pending.count) { // Node was not found. Add it new. SG_ERR_CHECK( _tree__add_new_node(pCtx, pTree, pNode, pszHidCandidate, &pNodeRef) ); } else if(iCandidate > iNode) { // Node was found further to the right in the tree. Steal it. SG_ERR_CHECK( _tree__move_node(pCtx, pTree->pending.p[iCandidate], pNode) ); // Also, remove it from the pending list. (It gets re-added later.) _node_list__remove_at(&pTree->pending, iCandidate); } else { // Node was found further to the left. Do nothing. } SG_ERR_CHECK( SG_vhash__add__int64(pCtx, pNode->pVcParents, pszHidCandidate, pNodeRef->revno) ); } // We have all this node's display children (still pending--they could get // stolen later). Now we need to sort them. if(pNode->displayChildren.count>1) { // First we pick one to go on the far left, if one stands out as most likely // to be the "old"/baseline node into which the others were "brought in". SG_uint32 iBaseline = pNode->displayChildren.count; // Allow the caller to have hand-picked the baseline node: if(pMergeBaselines!=NULL) { SG_int_to_string_buffer sz; SG_int64 baseline = 0; SG_ERR_CHECK( SG_vhash__check__int64(pCtx, pMergeBaselines, SG_int64_to_sz(pNode->revno, sz), &baseline) ); if(baseline!=0) { for(i=0; i<pNode->displayChildren.count; ++i) { if(pNode->displayChildren.p[i]->revno==(SG_uint32)baseline) { iBaseline = i; break; } } } } if(iBaseline == pNode->displayChildren.count) { // No baseline node from the user. See if there's one unique node whose // user *doesn't* match. for(i=0; i<pNode->displayChildren.count; ++i) { SG_bool match = SG_FALSE; SG_ERR_CHECK( _user_match_found(pCtx, pTree->pRepoRef, pNode->displayChildren.p[i], pNode, &match) ); if(!match) { if(iBaseline == pNode->displayChildren.count) { iBaseline = i; } else { // Whoops. "Nevermind." iBaseline = pNode->displayChildren.count; break; } } } } // Finally, sort _node_list__sort(&pNode->displayChildren, iBaseline); } // In the 'pending' list, replace this node with its children. if(pNode->displayChildren.count == 0) _node_list__remove_at(&pTree->pending, iNode); else { pTree->pending.p[iNode] = pNode->displayChildren.p[0]; if(pNode->displayChildren.count > 1) { SG_ERR_CHECK( _node_list__insert_at(pCtx, &pTree->pending, iNode+1, &pNode->displayChildren.p[1], pNode->displayChildren.count-1) ); } } // This node is no longer pending. pNode->isPending = SG_FALSE; return; fail: ; }
void SG_mrg_cset_entry_conflict__append_change(SG_context * pCtx, SG_mrg_cset_entry_conflict * pMrgCSetEntryConflict, SG_mrg_cset_entry * pMrgCSetEntry_Leaf_k, SG_mrg_cset_entry_neq neq) { SG_NULLARGCHECK_RETURN(pMrgCSetEntryConflict); SG_NULLARGCHECK_RETURN(pMrgCSetEntry_Leaf_k); if (!pMrgCSetEntryConflict->pVec_MrgCSetEntry_Changes) SG_ERR_CHECK_RETURN( SG_vector__alloc(pCtx,&pMrgCSetEntryConflict->pVec_MrgCSetEntry_Changes,2) ); SG_ERR_CHECK_RETURN( SG_vector__append(pCtx,pMrgCSetEntryConflict->pVec_MrgCSetEntry_Changes,(void *)pMrgCSetEntry_Leaf_k,NULL) ); if (!pMrgCSetEntryConflict->pVec_MrgCSetEntryNeq_Changes) SG_ERR_CHECK_RETURN( SG_vector_i64__alloc(pCtx,&pMrgCSetEntryConflict->pVec_MrgCSetEntryNeq_Changes,2) ); SG_ERR_CHECK_RETURN( SG_vector_i64__append(pCtx,pMrgCSetEntryConflict->pVec_MrgCSetEntryNeq_Changes,(SG_int64)neq,NULL) ); ////////////////////////////////////////////////////////////////// // add the value of the changed fields to the prbUnique_ rbtrees so that we can get a count of the unique new values. // ////////////////////////////////////////////////////////////////// // the values for RENAME, MOVE, ATTRBITS, SYMLINKS, and SUBMODULES are collapsable. that is, if we // have something like: // A // / \. // L0 a0 // / \. // L1 L2 // // and a rename in each Leaf, then we can either: // [a] prompt for them to choose L1 or L2's name and then // prompt for them to choose L0 or the name from step 1. // // [b] prompt for them to choose L0, L1, or L2 in one question. // // unlike file-content-merging, the net-net is that we have 1 new value // that is one of the inputs (or maybe we let them pick a new onw), but // it is not a combination of them and so we don't need to display the // immediate ancestor in the prompt. // // so we carry-forward the unique values from the leaves for each of // these fields. so the final merge-result may have more unique values // that it has direct parents. ////////////////////////////////////////////////////////////////// if (neq & SG_MRG_CSET_ENTRY_NEQ__ATTRBITS) { SG_int_to_string_buffer buf; SG_int64_to_sz((SG_int64)pMrgCSetEntry_Leaf_k->attrBits, buf); if (!pMrgCSetEntryConflict->prbUnique_AttrBits) SG_ERR_CHECK_RETURN( SG_RBTREE__ALLOC(pCtx,&pMrgCSetEntryConflict->prbUnique_AttrBits) ); SG_ERR_CHECK_RETURN( _update_1_rbUnique(pCtx,pMrgCSetEntryConflict->prbUnique_AttrBits,buf,pMrgCSetEntry_Leaf_k) ); if (pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict && pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_AttrBits) SG_ERR_CHECK_RETURN( _carry_forward_unique_values(pCtx, pMrgCSetEntryConflict->prbUnique_AttrBits, pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_AttrBits) ); } if (neq & SG_MRG_CSET_ENTRY_NEQ__ENTRYNAME) { if (!pMrgCSetEntryConflict->prbUnique_Entryname) SG_ERR_CHECK_RETURN( SG_RBTREE__ALLOC(pCtx,&pMrgCSetEntryConflict->prbUnique_Entryname) ); SG_ERR_CHECK_RETURN( _update_1_rbUnique(pCtx, pMrgCSetEntryConflict->prbUnique_Entryname, SG_string__sz(pMrgCSetEntry_Leaf_k->pStringEntryname), pMrgCSetEntry_Leaf_k) ); if (pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict && pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_Entryname) SG_ERR_CHECK_RETURN( _carry_forward_unique_values(pCtx, pMrgCSetEntryConflict->prbUnique_Entryname, pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_Entryname) ); } if (neq & SG_MRG_CSET_ENTRY_NEQ__GID_PARENT) { if (!pMrgCSetEntryConflict->prbUnique_GidParent) SG_ERR_CHECK_RETURN( SG_RBTREE__ALLOC(pCtx,&pMrgCSetEntryConflict->prbUnique_GidParent) ); SG_ERR_CHECK_RETURN( _update_1_rbUnique(pCtx,pMrgCSetEntryConflict->prbUnique_GidParent,pMrgCSetEntry_Leaf_k->bufGid_Parent,pMrgCSetEntry_Leaf_k) ); if (pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict && pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_GidParent) SG_ERR_CHECK_RETURN( _carry_forward_unique_values(pCtx, pMrgCSetEntryConflict->prbUnique_GidParent, pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_GidParent) ); } if (neq & SG_MRG_CSET_ENTRY_NEQ__SYMLINK_HID_BLOB) { if (!pMrgCSetEntryConflict->prbUnique_Symlink_HidBlob) SG_ERR_CHECK_RETURN( SG_RBTREE__ALLOC(pCtx,&pMrgCSetEntryConflict->prbUnique_Symlink_HidBlob) ); SG_ERR_CHECK_RETURN( _update_1_rbUnique(pCtx,pMrgCSetEntryConflict->prbUnique_Symlink_HidBlob,pMrgCSetEntry_Leaf_k->bufHid_Blob,pMrgCSetEntry_Leaf_k) ); if (pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict && pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_Symlink_HidBlob) SG_ERR_CHECK_RETURN( _carry_forward_unique_values(pCtx, pMrgCSetEntryConflict->prbUnique_Symlink_HidBlob, pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_Symlink_HidBlob) ); } if (neq & SG_MRG_CSET_ENTRY_NEQ__SUBMODULE_HID_BLOB) { if (!pMrgCSetEntryConflict->prbUnique_Submodule_HidBlob) SG_ERR_CHECK_RETURN( SG_RBTREE__ALLOC(pCtx,&pMrgCSetEntryConflict->prbUnique_Submodule_HidBlob) ); SG_ERR_CHECK_RETURN( _update_1_rbUnique(pCtx,pMrgCSetEntryConflict->prbUnique_Submodule_HidBlob,pMrgCSetEntry_Leaf_k->bufHid_Blob,pMrgCSetEntry_Leaf_k) ); if (pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict && pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_Submodule_HidBlob) SG_ERR_CHECK_RETURN( _carry_forward_unique_values(pCtx, pMrgCSetEntryConflict->prbUnique_Submodule_HidBlob, pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_Submodule_HidBlob) ); } // 2010/09/13 Update: we now do the carry-forward on the set of // unique HIDs for the various versions of the file // content from each of the leaves. This lets us // completely flatten the sub-merges into one final // result (with upto n values). // // This means we won't be creating the auto-merge-plan // at this point. // // The problem with the auto-merge-plan as originally // designed is that it was being driven based upon // the overall topology of the DAG as a whole rather // than the topology/history of the individual file. // And by respecting the history of the individual // file, I think we can get closer ancestors and better // per-file merging and perhaps fewer criss-crosses // and/or we push all of these issues to RESOLVE. if (neq & SG_MRG_CSET_ENTRY_NEQ__FILE_HID_BLOB) { if (!pMrgCSetEntryConflict->prbUnique_File_HidBlob) SG_ERR_CHECK_RETURN( SG_RBTREE__ALLOC(pCtx,&pMrgCSetEntryConflict->prbUnique_File_HidBlob) ); SG_ASSERT( (pMrgCSetEntry_Leaf_k->bufHid_Blob[0]) ); // TODO 2010/09/13 the code that sets __FILE_HID_BLOB probably cannot tell // TODO whether this branch did not change the file content // TODO relative to the LCA or whether it did change it back to // TODO the original value (an UNDO of the edits). I would argue // TODO that we should not list the former as a change, but that // TODO we SHOULD list the latter. The fix doesn't belong here, // TODO but this is just where I was typing when I thought of it. SG_ERR_CHECK_RETURN( _update_1_rbUnique(pCtx,pMrgCSetEntryConflict->prbUnique_File_HidBlob,pMrgCSetEntry_Leaf_k->bufHid_Blob,pMrgCSetEntry_Leaf_k) ); if (pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict && pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_File_HidBlob) SG_ERR_CHECK_RETURN( _carry_forward_unique_values(pCtx, pMrgCSetEntryConflict->prbUnique_File_HidBlob, pMrgCSetEntry_Leaf_k->pMrgCSetEntryConflict->prbUnique_File_HidBlob) ); } }