void sg_wc__status__find_status__single_item(SG_context * pCtx, const SG_varray * pva_statuses, const char * psz_repo_path, //This must be the repo path, exactly as it appears in the status results. SG_vhash ** ppvh_status_result) { SG_vhash * pvh_status = NULL; SG_uint32 nArraySize = 0; SG_uint32 index = 0; const char * psz_repo_path_to_check = NULL; SG_bool bFound = SG_FALSE; SG_NULLARGCHECK(pva_statuses); SG_ERR_CHECK( SG_varray__count(pCtx, pva_statuses, &nArraySize) ); for (index = 0; index < nArraySize; index++) { SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pva_statuses, index, &pvh_status) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh_status, "path", &psz_repo_path_to_check) ); if (SG_strcmp__null(psz_repo_path, psz_repo_path_to_check) == 0) { bFound = SG_TRUE; break; } } if (bFound) SG_RETURN_AND_NULL(pvh_status, ppvh_status_result); fail: return; }
/** * Given an issue that has a file-content conflict and has just been resolved, * go thru all of the steps in the merge plan and delete the ~mine files. */ static void _resolve__delete_temp_files(SG_context * pCtx, struct _resolve_data * pData, const char * pszGid, const SG_vhash * pvhIssue) { _resolve__step_pathnames * pStepPathnames = NULL; const SG_varray * pvaPlan; SG_uint32 kStep, nrSteps; SG_UNUSED( pszGid ); SG_ERR_CHECK( SG_vhash__get__varray(pCtx, pvhIssue, "conflict_file_merge_plan", (SG_varray **)&pvaPlan) ); SG_ERR_CHECK( SG_varray__count(pCtx, pvaPlan, &nrSteps) ); for (kStep=0; kStep<nrSteps; kStep++) { const SG_vhash * pvhStep; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaPlan, kStep, (SG_vhash **)&pvhStep) ); SG_ERR_CHECK( _resolve__step_pathnames__compute(pCtx, pData, pvhIssue, pvhStep, NULL, &pStepPathnames) ); SG_ERR_CHECK( _resolve__step_pathnames__delete_temp_files(pCtx, pStepPathnames) ); _RESOLVE__STEP_PATHNAMES__NULLFREE(pCtx, pStepPathnames); } fail: _RESOLVE__STEP_PATHNAMES__NULLFREE(pCtx, pStepPathnames); }
static void _user_match_found(SG_context * pCtx, SG_repo * pRepo, _node_t *pNode1, _node_t * pNode2, SG_bool *matchFound) { SG_uint32 countAudits1 = 0; SG_uint32 countAudits2 = 0; SG_uint32 iAudit1; if(pNode1->pAudits==NULL) { SG_ERR_CHECK( SG_repo__lookup_audits(pCtx, pRepo, SG_DAGNUM__VERSION_CONTROL, pNode1->pszHidRef, &pNode1->pAudits) ); } if(pNode2->pAudits==NULL) { SG_ERR_CHECK( SG_repo__lookup_audits(pCtx, pRepo, SG_DAGNUM__VERSION_CONTROL, pNode2->pszHidRef, &pNode2->pAudits) ); } SG_ERR_CHECK( SG_varray__count(pCtx, pNode1->pAudits, &countAudits1) ); SG_ERR_CHECK( SG_varray__count(pCtx, pNode2->pAudits, &countAudits2) ); for(iAudit1=0; iAudit1<countAudits1; ++iAudit1) { SG_vhash * pAuditRef = NULL; SG_uint32 iAudit2; const char * szAudit1 = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pNode1->pAudits, iAudit1, &pAuditRef) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pAuditRef, "userid", &szAudit1) ); for(iAudit2=0; iAudit2<countAudits2; ++iAudit2) { const char * szAudit2 = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pNode2->pAudits, iAudit2, &pAuditRef) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pAuditRef, "userid", &szAudit2) ); if(strcmp(szAudit1, szAudit2)==0) { *matchFound = SG_TRUE; return; } } } *matchFound = SG_FALSE; return; fail: ; }
/** * build an ordered stringarray of the GIDs of all of the issues. * i need to have the list of GIDs be independent of pPendingTree * and pvaIssues so that FIX can do incremental SAVES of the * pendingtree (which trashes the ptnodes and/or requires a reload). */ static void _resolve__get_all_issue_gids(SG_context * pCtx, struct _resolve_data * pData, SG_bool bWantResolved, SG_bool bWantUnresolved) { const SG_varray * pvaIssues; // varray[pvhIssue *] of all issues -- we do not own this SG_bool bHaveIssues; SG_uint32 k; SG_uint32 nrIssues = 0; SG_bool bWantBoth = (bWantResolved && bWantUnresolved); SG_ERR_CHECK( SG_pendingtree__get_wd_issues__ref(pCtx, pData->pPendingTree, &bHaveIssues, &pvaIssues) ); if (bHaveIssues) SG_ERR_CHECK( SG_varray__count(pCtx, pvaIssues, &nrIssues) ); SG_ERR_CHECK( SG_STRINGARRAY__ALLOC(pCtx, &pData->psaGids, nrIssues) ); for (k=0; k<nrIssues; k++) { const SG_vhash * pvhIssue_k; const char * pszGid_k; SG_bool bWantThisOne; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaIssues, k, (SG_vhash **)&pvhIssue_k) ); if (bWantBoth) bWantThisOne = SG_TRUE; else { SG_int64 s; SG_pendingtree_wd_issue_status status; SG_bool bResolved; SG_ERR_CHECK_RETURN( SG_vhash__get__int64(pCtx, pvhIssue_k, "status", &s) ); status = (SG_pendingtree_wd_issue_status)s; bResolved = ((status & SG_ISSUE_STATUS__MARKED_RESOLVED) == SG_ISSUE_STATUS__MARKED_RESOLVED); bWantThisOne = ((bWantResolved && bResolved) || (bWantUnresolved && !bResolved)); } if (bWantThisOne) { SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhIssue_k, "gid", &pszGid_k) ); SG_ERR_CHECK( SG_stringarray__add(pCtx, pData->psaGids, pszGid_k) ); } } fail: return; }
/** * Pick thru the computed VARRAY build a subset VARRAY * containing just the dirty files. That is, for an interactive * diff, we only show dirty files. (In batch/patch mode, we show * everything.) * * We assume that the varray looks like: * * varray := [ { "status" : { "flags" : <int>, * ... }, * "path" : <repo-path>, * ... }, * ... ]; * * Both a Canonical STATUS (pvaStatus) and a "DiffStep" (pvaDiffStep) * match this pattern. * * Return NULL if there aren't any. * */ static void _get_dirty_files(SG_context * pCtx, const SG_varray * pva, SG_varray ** ppvaDirtyFiles) { SG_varray * pvaDirtyFiles = NULL; SG_uint32 k, nrItems; *ppvaDirtyFiles = NULL; if (!pva) return; SG_ERR_CHECK( SG_varray__count(pCtx, pva, &nrItems) ); if (nrItems == 0) return; for (k=0; k<nrItems; k++) { SG_vhash * pvhItem_k; // we do not own this SG_vhash * pvhItemStatus_k; // we do not own this SG_int64 i64; SG_wc_status_flags statusFlags; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pva, k, &pvhItem_k) ); SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhItem_k, "status", &pvhItemStatus_k) ); SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvhItemStatus_k, "flags", &i64) ); statusFlags = (SG_wc_status_flags)i64; if ((statusFlags & SG_WC_STATUS_FLAGS__T__FILE) == 0) continue; if ((statusFlags & (SG_WC_STATUS_FLAGS__C__NON_DIR_MODIFIED |SG_WC_STATUS_FLAGS__S__ADDED |SG_WC_STATUS_FLAGS__S__DELETED |SG_WC_STATUS_FLAGS__S__MERGE_CREATED |SG_WC_STATUS_FLAGS__S__UPDATE_CREATED)) == 0) continue; if (!pvaDirtyFiles) SG_ERR_CHECK( SG_VARRAY__ALLOC(pCtx, &pvaDirtyFiles) ); SG_ERR_CHECK( SG_varray__appendcopy__vhash(pCtx, pvaDirtyFiles, pvhItem_k, NULL) ); } SG_RETURN_AND_NULL( pvaDirtyFiles, ppvaDirtyFiles ); fail: SG_VARRAY_NULLFREE(pCtx, pvaDirtyFiles); }
/** * Our caller is trying to create new repo and create a WD * mapped to it. The destination directory may or may not * have already existed on disk before we started. If we are * building upon an existing directory, verify that it doesn't * contain any submodules because we don't yet support them. * */ static void _check_for_nested_drawer(SG_context * pCtx, SG_wc_tx * pWcTx) { SG_varray * pvaStatus = NULL; SG_string * pString_MyDrawerRepoPath = NULL; SG_string * pString_MatchedRepoPath = NULL; const char * psz_MyDrawerName = NULL; const char * psz_MyDrawerRepoPath = NULL; SG_uint32 k, nrItems; if (pWcTx->bWeCreated_WD || pWcTx->bWeCreated_WD_Contents) return; SG_ERR_CHECK( SG_wc_tx__status(pCtx, pWcTx, NULL, SG_UINT32_MAX, SG_FALSE, // bListUnchanged SG_TRUE, // bNoIgnores SG_TRUE, // bNoTSC, SG_FALSE, // bListSparse SG_TRUE, // bListReserved SG_TRUE, // bNoSort, &pvaStatus, NULL) ); if (!pvaStatus) return; // TODO 2012/11/13 For now I'm just going to see if there is a // TODO .sgdrawer somewhere within the directory tree. // TODO In theory, we could have ADD/ADDREMOVE just // TODO look for them and refuse to add its parent // TODO directory, but I don't to even support that // TODO until we've properly dealt with submodules. // TODO // TODO So for now, if there is a WD deeply nested within // TODO this directory, we just complain. This is mainly // TODO to prevent accidents. (Because they can still // TODO manually move a sub-WD somehere deep into this // TODO directory at some point in the future.) SG_ERR_CHECK( SG_workingdir__get_drawer_directory_name(pCtx, &psz_MyDrawerName) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pString_MyDrawerRepoPath) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pString_MyDrawerRepoPath, "@/%s", psz_MyDrawerName) ); SG_ERR_CHECK( SG_repopath__ensure_final_slash(pCtx, pString_MyDrawerRepoPath) ); psz_MyDrawerRepoPath = SG_string__sz(pString_MyDrawerRepoPath); SG_ERR_CHECK( SG_varray__count(pCtx, pvaStatus, &nrItems) ); for (k=0; k<nrItems; k++) { SG_vhash * pvhItem; SG_vhash * pvhItemStatus; SG_bool bIsReserved; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaStatus, k, &pvhItem) ); SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhItem, "status", &pvhItemStatus) ); SG_ERR_CHECK( SG_vhash__has(pCtx, pvhItemStatus, "isReserved", &bIsReserved) ); if (bIsReserved) { // Don't freak out over the .sgdrawer that we just created in the root. const char * pszRepoPath; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhItem, "path", &pszRepoPath) ); if (strcmp(pszRepoPath, psz_MyDrawerRepoPath) != 0) { SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pString_MatchedRepoPath, pszRepoPath) ); SG_ERR_CHECK( SG_repopath__remove_last(pCtx, pString_MatchedRepoPath) ); SG_ERR_THROW2( SG_ERR_ENTRY_ALREADY_UNDER_VERSION_CONTROL, (pCtx, "The directory '%s' contains a working copy and submodules are not yet supported.", SG_string__sz(pString_MatchedRepoPath)) ); } } } fail: SG_STRING_NULLFREE(pCtx, pString_MatchedRepoPath); SG_STRING_NULLFREE(pCtx, pString_MyDrawerRepoPath); SG_VARRAY_NULLFREE(pCtx, pvaStatus); }
/** * Compute DIFF on (baseline or arbitrary cset vs WC) and either * splat to console or launch a GUI tool for each. * */ static void _s01__do_cset_vs_wc(SG_context * pCtx, const SG_option_state * pOptSt, const SG_stringarray * psaArgs, SG_uint32 * pNrErrors) { SG_wc_tx * pWcTx = NULL; SG_varray * pvaDiffSteps = NULL; SG_varray * pvaDiffSteps_DirtyFiles = NULL; SG_pathname * pPathWc = NULL; SG_uint32 nrErrors = 0; SG_ERR_CHECK( SG_WC_TX__ALLOC__BEGIN(pCtx, &pWcTx, pPathWc, SG_TRUE) ); SG_ERR_CHECK( SG_wc_tx__diff__setup__stringarray(pCtx, pWcTx, pOptSt->pRevSpec, psaArgs, WC__GET_DEPTH(pOptSt), SG_FALSE, // bNoIgnores SG_FALSE, // bNoTSC SG_FALSE, // bNoSort, pOptSt->bInteractive, pOptSt->psz_tool, &pvaDiffSteps) ); // rollback/cancel the TX to release the SQL locks, // but don't free it yet (because that will auto-delete // the session temp files that we are using for the left // sides). // // This is like SG_wc__diff__throw() but we want to control // the diff-loop so we can optionally do the interactive prompt. SG_ERR_CHECK( SG_wc_tx__cancel(pCtx, pWcTx) ); if (pvaDiffSteps) { if (pOptSt->bInteractive) { SG_ERR_CHECK( _get_dirty_files(pCtx, pvaDiffSteps, &pvaDiffSteps_DirtyFiles) ); if (pvaDiffSteps_DirtyFiles) SG_ERR_CHECK( _do_gui_diffs(pCtx, SG_TRUE, pOptSt, pvaDiffSteps_DirtyFiles, &nrErrors) ); } else { SG_uint32 k, nrItems; SG_ERR_CHECK( SG_varray__count(pCtx, pvaDiffSteps, &nrItems) ); for (k=0; k<nrItems; k++) { SG_vhash * pvhItem; const char * pszHeader = NULL; SG_uint32 iResult; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaDiffSteps, k, &pvhItem) ); SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pvhItem, "header", &pszHeader) ); if (pszHeader) SG_ERR_IGNORE( SG_console__raw(pCtx, SG_CS_STDOUT, pszHeader) ); SG_ERR_CHECK( _do_diff1(pCtx, SG_TRUE, pOptSt, pvhItem, &iResult) ); switch (iResult) { default: case SG_FILETOOL__RESULT__SUCCESS: case SG_DIFFTOOL__RESULT__SAME: case SG_DIFFTOOL__RESULT__DIFFERENT: break; case SG_DIFFTOOL__RESULT__CANCEL: case SG_FILETOOL__RESULT__FAILURE: case SG_FILETOOL__RESULT__ERROR: nrErrors++; break; } } } } *pNrErrors = nrErrors; fail: SG_VARRAY_NULLFREE(pCtx, pvaDiffSteps); SG_VARRAY_NULLFREE(pCtx, pvaDiffSteps_DirtyFiles); SG_WC_TX__NULLFREE(pCtx, pWcTx); }
static void _s2__do_cset_vs_cset(SG_context * pCtx, const SG_option_state * pOptSt, const SG_stringarray * psaArgs, SG_uint32 * pNrErrors) { SG_varray * pvaStatus = NULL; SG_varray * pvaStatusDirtyFiles = NULL; SG_stringarray * psa1 = NULL; SG_string * pStringGidRepoPath = NULL; SG_string * pStringErr = NULL; SG_uint32 nrErrors = 0; SG_ERR_CHECK( SG_vv2__status(pCtx, pOptSt->psz_repo, pOptSt->pRevSpec, psaArgs, WC__GET_DEPTH(pOptSt), SG_FALSE, // bNoSort &pvaStatus, NULL) ); if (pvaStatus) { if (pOptSt->bInteractive) { // Filter list down to just modified files and show them one-by-one. SG_ERR_CHECK( _get_dirty_files(pCtx, pvaStatus, &pvaStatusDirtyFiles) ); if (pvaStatusDirtyFiles) SG_ERR_CHECK( _do_gui_diffs(pCtx, SG_FALSE, pOptSt, pvaStatusDirtyFiles, &nrErrors) ); } else { SG_uint32 k, nrItems; // Print the changes with PATCH-like headers. // Accumulate any tool errors. SG_ERR_CHECK( SG_varray__count(pCtx, pvaStatus, &nrItems) ); for (k=0; k<nrItems; k++) { SG_vhash * pvhItem; const char * pszGid = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaStatus, k, &pvhItem) ); // TODO 2013/02/22 Our pvhItem has all of the details for the diff, // TODO but we don't yet have a public API to let it be // TODO used as is. So we build a @gid repo-path and // TODO run the old historical diff code on a 1-item array // TODO containing this @gid. // TODO // TODO We should fix this to just pass down the pvhItem // TOOD so that it doesn't have to repeat the status lookup. SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhItem, "gid", &pszGid) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pStringGidRepoPath) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pStringGidRepoPath, "@%s", pszGid) ); SG_ERR_CHECK( SG_STRINGARRAY__ALLOC(pCtx, &psa1, 1) ); SG_ERR_CHECK( SG_stringarray__add(pCtx, psa1, SG_string__sz(pStringGidRepoPath)) ); SG_vv2__diff_to_stream__throw(pCtx, pOptSt->psz_repo, pOptSt->pRevSpec, psa1, 0, SG_TRUE, // bNoSort -- doesn't matter only 1 item in list SG_FALSE, // bInteractive, pOptSt->psz_tool); // Don't throw the error from the tool. Just print it on STDERR // and remember that we had an error so that don't stop showing // the diffs just because we stumble over a changed binary file // or mis-configured tool, for example. if (SG_context__has_err(pCtx)) { SG_context__err_to_string(pCtx, SG_FALSE, &pStringErr); SG_context__err_reset(pCtx); SG_ERR_CHECK( SG_console__raw(pCtx, SG_CS_STDERR, SG_string__sz(pStringErr)) ); SG_STRING_NULLFREE(pCtx, pStringErr); nrErrors++; } SG_STRING_NULLFREE(pCtx, pStringGidRepoPath); SG_STRINGARRAY_NULLFREE(pCtx, psa1); } } } *pNrErrors = nrErrors; fail: SG_VARRAY_NULLFREE(pCtx, pvaStatus); SG_VARRAY_NULLFREE(pCtx, pvaStatusDirtyFiles); SG_STRINGARRAY_NULLFREE(pCtx, psa1); SG_STRING_NULLFREE(pCtx, pStringGidRepoPath); SG_STRING_NULLFREE(pCtx, pStringErr); }
/** * Iterate over all of the dirty files and prompt before diffing. * pvaDirtyFiles can either be a STATUS or a "DiffStep" varray. * */ static void _do_gui_diffs(SG_context * pCtx, SG_bool bWC, const SG_option_state * pOptSt, const SG_varray * pvaDirtyFiles, SG_uint32 * pNrErrors) { SG_uint32 k, nrItems; SG_uint32 nrErrors = 0; SG_ERR_CHECK( SG_varray__count(pCtx, pvaDirtyFiles, &nrItems) ); if (nrItems == 1) // if only 1 item, no fancy prompt required. { SG_vhash * pvhItem_0; // we do not own this SG_uint32 iResult = 0; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaDirtyFiles, 0, &pvhItem_0) ); SG_ERR_CHECK( _do_diff1(pCtx, bWC, pOptSt, pvhItem_0, &iResult) ); switch (iResult) { default: case SG_FILETOOL__RESULT__SUCCESS: case SG_DIFFTOOL__RESULT__SAME: case SG_DIFFTOOL__RESULT__DIFFERENT: break; case SG_DIFFTOOL__RESULT__CANCEL: case SG_FILETOOL__RESULT__FAILURE: case SG_FILETOOL__RESULT__ERROR: nrErrors++; break; } } else { k = 0; while (1) { SG_vhash * pvhItem; // we do not own this const char * pszRepoPath; char chChoice = 'd'; SG_uint32 iResult; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaDirtyFiles, k, &pvhItem) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhItem, "path", &pszRepoPath) ); SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDOUT, "\n[%d/%d] %s:\n", k+1, nrItems, pszRepoPath) ); if (k == 0) SG_ERR_CHECK( _do_prompt(pCtx, "(d)iff (n)ext (q)uit", "dnq", 'd', &chChoice) ); else if (k+1 == nrItems) SG_ERR_CHECK( _do_prompt(pCtx, "(d)iff (p)rev (q)uit", "dpq", 'd', &chChoice) ); else SG_ERR_CHECK( _do_prompt(pCtx, "(d)iff (n)ext (p)rev (q)uit", "dnpq", 'd', &chChoice) ); switch (chChoice) { case 'd': SG_ERR_CHECK( _do_diff1(pCtx, bWC, pOptSt, pvhItem, &iResult) ); switch (iResult) { default: case SG_FILETOOL__RESULT__SUCCESS: case SG_DIFFTOOL__RESULT__SAME: case SG_DIFFTOOL__RESULT__DIFFERENT: // advance to next pair of files or finish. if (k+1 == nrItems) goto done; k++; break; case SG_DIFFTOOL__RESULT__CANCEL: case SG_FILETOOL__RESULT__FAILURE: case SG_FILETOOL__RESULT__ERROR: nrErrors++; // stay on this pair of files (so that they see the // error message and this filename again). break; } break; case 'n': k++; break; case 'p': k--; break; default: case 'q': goto done; } } } done: *pNrErrors = nrErrors; fail: return; }
/** * Release VFILE lock and invoke external merge tool for this file. * * TODO 2010/07/12 The MERGE-PLAN is an array and allows for * TODO multiple steps (for an n-way sub-merge cascade). * TODO But we don't have that part turned on yet in * TODO sg_mrg__private_biuld_wd_issues.h:_make_file_merge_plan(), * TODO so for now, we only expect 1 step. * TODO * TODO Also, when we do have multiple steps, we might want to * TODO be able to use the 'status' field to see which steps * TODO were already performed in an earlier RESOLVE. * TODO * TODO Also, when we want to support more than 1 step we need * TODO to copy pvaPlan because when we release the pendingtree * TODO the pvhIssue becomes invalidated too. */ static void _resolve__fix__run_external_file_merge(SG_context * pCtx, struct _resolve_data * pData, const char * pszGid, const SG_vhash * pvhIssue, SG_string * pStrRepoPath, enum _fix_status * pFixStatus) { _resolve__step_pathnames * pStepPathnames = NULL; _resolve__external_tool * pET = NULL; const SG_varray * pvaPlan; const SG_vhash * pvhStep_0; SG_int64 r64; SG_uint32 nrSteps; SG_mrg_automerge_result result; SG_bool bMerged = SG_FALSE; SG_bool bIsResolved = SG_FALSE; SG_ERR_CHECK( SG_vhash__get__varray(pCtx, pvhIssue, "conflict_file_merge_plan", (SG_varray **)&pvaPlan) ); SG_ERR_CHECK( SG_varray__count(pCtx, pvaPlan, &nrSteps) ); if (nrSteps > 1) SG_ERR_THROW2( SG_ERR_ASSERT, (pCtx, "TODO RESOLVE more than 1 step in auto-merge plan for '%s'.", SG_string__sz(pStrRepoPath)) ); ////////////////////////////////////////////////////////////////// // Get Step[0] SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaPlan, 0, (SG_vhash **)&pvhStep_0) ); // see if the user has already performed the merge and maybe got interrupted. SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvhStep_0, "status", &r64) ); result = (SG_mrg_automerge_result)r64; if (result == SG_MRG_AUTOMERGE_RESULT__SUCCESSFUL) { SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "TODO Print message about previous successful manual merge of the file content and ask if they want to redo it for '%s'.\n", SG_string__sz(pStrRepoPath)) ); *pFixStatus = FIX_USER_MERGED; goto done; } SG_ERR_CHECK( _resolve__step_pathnames__compute(pCtx, pData, pvhIssue, pvhStep_0, pStrRepoPath, &pStepPathnames) ); // While we still have a handle to the pendingtree, lookup the // specifics on the external tool that we should invoke. these // details come from localsettings. SG_ERR_CHECK( _resolve__external_tool__lookup(pCtx, pData, pszGid, pvhIssue, pStrRepoPath, &pET) ); // Free the PENDINGTREE so that we release the VFILE lock. pvhIssue = NULL; pvaPlan = NULL; pvhStep_0 = NULL; SG_PENDINGTREE_NULLFREE(pCtx, pData->pPendingTree); ////////////////////////////////////////////////////////////////// // Invoke the external tool. SG_ERR_CHECK( _resolve__fix__run_external_file_merge_1(pCtx, pData, pET, pStepPathnames, pStrRepoPath, &bMerged) ); if (!bMerged) { SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDOUT, "RESOLVE: Aborting the merge of this file.\n") ); *pFixStatus = FIX_USER_ABORTED; goto done; } ////////////////////////////////////////////////////////////////// // Reload the PENDINGTREE and re-fetch the ISSUE and updated the STATUS on // this step in the PLAN. // // We duplicate some of the "see if someone else resolved this issue while // we were without the lock" stuff. SG_ERR_CHECK( _resolve__lookup_issue(pCtx, pData, pszGid, &pvhIssue) ); SG_ERR_CHECK( _resolve__is_resolved(pCtx, pvhIssue, &bIsResolved) ); if (bIsResolved) { // Someone else marked it resolved while were waiting for // the user to edit the file and while we didn't have the // file lock. We should stop here. SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDOUT, "RESOLVE: Aborting the merge of this file (due to race condition).\n") ); *pFixStatus = FIX_LOST_RACE; goto done; } // re-fetch the current step and update the "result" status for it // and flush the pendingtree back disk. // // we only update the step status -- we DO NOT alter the __DIVERGENT_FILE_EDIT__ // conflict_flags. SG_ERR_CHECK( SG_vhash__get__varray(pCtx, pvhIssue, "conflict_file_merge_plan", (SG_varray **)&pvaPlan) ); SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaPlan, 0, (SG_vhash **)&pvhStep_0) ); SG_ERR_CHECK( SG_pendingtree__set_wd_issue_plan_step_status__dont_save_pendingtree(pCtx, pData->pPendingTree, pvhStep_0, SG_MRG_AUTOMERGE_RESULT__SUCCESSFUL) ); SG_ERR_CHECK( SG_pendingtree__save(pCtx, pData->pPendingTree) ); SG_PENDINGTREE_NULLFREE(pCtx, pData->pPendingTree); SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDOUT, "RESOLVE: The file content portion of the merge was successful.\n") ); *pFixStatus = FIX_USER_MERGED; // we defer the delete of the temp input files until we completely // resolve the issue. (This gives us more options if we allow the // resolve to be restarted after interruptions.) done: ; fail: _RESOLVE__EXTERNAL_TOOL__NULLFREE(pCtx, pET); _RESOLVE__STEP_PATHNAMES__NULLFREE(pCtx, pStepPathnames); }
void SG_cmd_util__get_username_for_repo( SG_context *pCtx, const char *szRepoName, char **ppUsername ) { SG_string * pUsername = NULL; SG_repo * pRepo = NULL; char * psz_username = NULL; SG_curl * pCurl = NULL; SG_string * pUri = NULL; SG_string * pResponse = NULL; SG_int32 responseStatusCode = 0; SG_vhash * pRepoInfo = NULL; char * psz_userid = NULL; SG_varray * pUsers = NULL; SG_NULLARGCHECK_RETURN(ppUsername); if(!szRepoName) { // Look up username based on 'whoami' of repo associated with cwd. SG_ERR_IGNORE( SG_cmd_util__get_repo_from_cwd(pCtx, &pRepo, NULL) ); if(pRepo) SG_ERR_IGNORE( SG_user__get_username_for_repo(pCtx, pRepo, &psz_username) ); SG_REPO_NULLFREE(pCtx, pRepo); } else if(SG_sz__starts_with(szRepoName, "http://") || SG_sz__starts_with(szRepoName, "https://")) { // Look up username based on 'whoami' of admin id of remote repo. SG_ERR_CHECK( SG_curl__alloc(pCtx, &pCurl) ); SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pUri, szRepoName) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pUri, ".json") ); SG_ERR_CHECK( SG_curl__reset(pCtx, pCurl) ); SG_ERR_CHECK( SG_curl__setopt__sz(pCtx, pCurl, CURLOPT_URL, SG_string__sz(pUri)) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pResponse) ); SG_ERR_CHECK( SG_curl__set__write_string(pCtx, pCurl, pResponse) ); SG_ERR_CHECK( SG_curl__perform(pCtx, pCurl) ); SG_ERR_CHECK( SG_curl__getinfo__int32(pCtx, pCurl, CURLINFO_RESPONSE_CODE, &responseStatusCode) ); if(responseStatusCode==200) { const char * szAdminId = NULL; SG_ERR_CHECK( SG_VHASH__ALLOC__FROM_JSON__STRING(pCtx, &pRepoInfo, pResponse) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pRepoInfo, SG_SYNC_REPO_INFO_KEY__ADMIN_ID, &szAdminId) ); SG_ERR_CHECK( SG_string__clear(pCtx, pUri) ); SG_ERR_CHECK( SG_string__append__format(pCtx, pUri, "/admin/%s/whoami/userid", szAdminId) ); SG_ERR_IGNORE( SG_localsettings__get__sz(pCtx, SG_string__sz(pUri), NULL, &psz_userid, NULL) ); if(psz_userid) { // We now have the userid. Look up the username. SG_ERR_CHECK( SG_string__clear(pCtx, pUri) ); SG_ERR_CHECK( SG_string__append__format(pCtx, pUri, "%s/users.json", szRepoName) ); SG_ERR_CHECK( SG_curl__reset(pCtx, pCurl) ); SG_ERR_CHECK( SG_curl__setopt__sz(pCtx, pCurl, CURLOPT_URL, SG_string__sz(pUri)) ); SG_ERR_CHECK( SG_string__clear(pCtx, pResponse) ); SG_ERR_CHECK( SG_curl__set__write_string(pCtx, pCurl, pResponse) ); SG_ERR_CHECK( SG_curl__perform(pCtx, pCurl) ); SG_ERR_CHECK( SG_curl__getinfo__int32(pCtx, pCurl, CURLINFO_RESPONSE_CODE, &responseStatusCode) ); if(responseStatusCode==200) { SG_uint32 i, nUsers; SG_ERR_CHECK( SG_VARRAY__ALLOC__FROM_JSON__STRING(pCtx, &pUsers, pResponse) ); SG_ERR_CHECK( SG_varray__count(pCtx, pUsers, &nUsers) ); for(i=0; i<nUsers; ++i) { SG_vhash * pUser = NULL; const char * psz_recid = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pUsers, i, &pUser) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pUser, "recid", &psz_recid) ); if(!strcmp(psz_recid, psz_userid)) { const char * psz_name = NULL; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pUser, "name", &psz_name) ); SG_ERR_CHECK( SG_STRDUP(pCtx, psz_name, &psz_username) ); break; } } SG_VARRAY_NULLFREE(pCtx, pUsers); } SG_NULLFREE(pCtx, psz_userid); } SG_VHASH_NULLFREE(pCtx, pRepoInfo); } SG_STRING_NULLFREE(pCtx, pResponse); SG_STRING_NULLFREE(pCtx, pUri); SG_CURL_NULLFREE(pCtx, pCurl); } else { // Look up username based on 'whoami' of repo provided. SG_ERR_CHECK( SG_REPO__OPEN_REPO_INSTANCE(pCtx, szRepoName, &pRepo) ); SG_ERR_IGNORE( SG_user__get_username_for_repo(pCtx, pRepo, &psz_username) ); SG_REPO_NULLFREE(pCtx, pRepo); } *ppUsername = psz_username; return; fail: SG_STRING_NULLFREE(pCtx, pUsername); SG_REPO_NULLFREE(pCtx, pRepo); SG_NULLFREE(pCtx, psz_username); SG_CURL_NULLFREE(pCtx, pCurl); SG_STRING_NULLFREE(pCtx, pUri); SG_STRING_NULLFREE(pCtx, pResponse); SG_VHASH_NULLFREE(pCtx, pRepoInfo); SG_NULLFREE(pCtx, psz_userid); SG_VARRAY_NULLFREE(pCtx, pUsers); }
int u0050_logstuff_test__1(SG_context * pCtx, SG_pathname* pPathTopDir) { char bufName[SG_TID_MAX_BUFFER_LENGTH]; SG_pathname* pPathWorkingDir = NULL; SG_pathname* pPathFile = NULL; SG_vhash* pvh = NULL; SG_dagnode* pdn = NULL; const char* psz_hid_cs = NULL; SG_repo* pRepo = NULL; SG_uint32 count; SG_rbtree* prb = NULL; SG_varray* pva = NULL; SG_rbtree* prb_reversed = NULL; const char* psz_val = NULL; SG_audit q; VERIFY_ERR_CHECK( SG_tid__generate2(pCtx, bufName, sizeof(bufName), 32) ); /* create the working dir */ VERIFY_ERR_CHECK( SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPathWorkingDir, pPathTopDir, bufName) ); VERIFY_ERR_CHECK( SG_fsobj__mkdir__pathname(pCtx, pPathWorkingDir) ); /* add stuff */ VERIFY_ERR_CHECK( u0050_logstuff__create_file__numbers(pCtx, pPathWorkingDir, "aaa", 20) ); /* create the repo */ VERIFY_ERR_CHECK( _ut_pt__new_repo(pCtx, bufName, pPathWorkingDir) ); VERIFY_ERR_CHECK( _ut_pt__addremove(pCtx, pPathWorkingDir) ); VERIFY_ERR_CHECK( u0050_logstuff__commit_all(pCtx, pPathWorkingDir, &pdn) ); VERIFY_ERR_CHECK( SG_dagnode__get_id_ref(pCtx, pdn, &psz_hid_cs) ); SG_ERR_CHECK( SG_repo__open_repo_instance(pCtx, bufName, &pRepo) ); #define MY_COMMENT "The name of this new file sucks! What kind of a name is 'aaa'?" VERIFY_ERR_CHECK( SG_audit__init(pCtx, &q, pRepo, SG_AUDIT__WHEN__NOW, SG_AUDIT__WHO__FROM_SETTINGS) ); VERIFY_ERR_CHECK( SG_vc_comments__add(pCtx, pRepo, psz_hid_cs, MY_COMMENT, &q) ); VERIFY_ERR_CHECK( SG_vc_stamps__add(pCtx, pRepo, psz_hid_cs, "crap", &q) ); VERIFY_ERR_CHECK( SG_vc_tags__add(pCtx, pRepo, psz_hid_cs, "tcrap", &q) ); VERIFY_ERR_CHECK( SG_vc_comments__lookup(pCtx, pRepo, psz_hid_cs, &pva) ); VERIFY_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); VERIFY_COND("count", (1 == count)); VERIFY_ERR_CHECK( SG_varray__get__vhash(pCtx, pva, 0, &pvh) ); VERIFY_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh, "text", &psz_val) ); VERIFY_COND("match", (0 == strcmp(psz_val, MY_COMMENT)) ); SG_VARRAY_NULLFREE(pCtx, pva); VERIFY_ERR_CHECK( SG_vc_stamps__lookup(pCtx, pRepo, psz_hid_cs, &pva) ); VERIFY_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); VERIFY_COND("count", (1 == count)); VERIFY_ERR_CHECK( SG_varray__get__vhash(pCtx, pva, 0, &pvh) ); VERIFY_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh, "stamp", &psz_val) ); VERIFY_COND("match", (0 == strcmp(psz_val, "crap")) ); SG_VARRAY_NULLFREE(pCtx, pva); VERIFY_ERR_CHECK( SG_vc_tags__lookup(pCtx, pRepo, psz_hid_cs, &pva) ); VERIFY_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); VERIFY_COND("count", (1 == count)); VERIFY_ERR_CHECK( SG_varray__get__vhash(pCtx, pva, 0, &pvh) ); VERIFY_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh, "tag", &psz_val) ); VERIFY_COND("match", (0 == strcmp(psz_val, "tcrap")) ); SG_VARRAY_NULLFREE(pCtx, pva); VERIFY_ERR_CHECK( SG_vc_tags__list(pCtx, pRepo, &prb) ); VERIFY_ERR_CHECK( SG_rbtree__count(pCtx, prb, &count) ); VERIFY_COND("count", (1 == count)); VERIFY_ERR_CHECK( SG_vc_tags__build_reverse_lookup(pCtx, prb, &prb_reversed) ); VERIFY_ERR_CHECK( SG_rbtree__count(pCtx, prb_reversed, &count) ); VERIFY_COND("count", (1 == count)); { const char* psz_my_key = NULL; const char* psz_my_val = NULL; SG_bool b; VERIFY_ERR_CHECK( SG_rbtree__iterator__first(pCtx, NULL, prb_reversed, &b, &psz_my_key, (void**) &psz_my_val) ); VERIFY_COND("ok", (0 == strcmp(psz_my_val, "tcrap")) ); VERIFY_COND("ok", (0 == strcmp(psz_my_key, psz_hid_cs)) ); } SG_RBTREE_NULLFREE(pCtx, prb_reversed); SG_RBTREE_NULLFREE(pCtx, prb); VERIFY_ERR_CHECK( SG_vc_tags__add(pCtx, pRepo, psz_hid_cs, "whatever", &q) ); VERIFY_ERR_CHECK( SG_vc_tags__lookup(pCtx, pRepo, psz_hid_cs, &pva) ); VERIFY_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); VERIFY_COND("count", (2 == count)); SG_VARRAY_NULLFREE(pCtx, pva); VERIFY_ERR_CHECK( SG_vc_tags__list(pCtx, pRepo, &prb) ); VERIFY_ERR_CHECK( SG_rbtree__count(pCtx, prb, &count) ); VERIFY_COND("count", (2 == count)); VERIFY_ERR_CHECK( SG_vc_tags__build_reverse_lookup(pCtx, prb, &prb_reversed) ); VERIFY_ERR_CHECK( SG_rbtree__count(pCtx, prb_reversed, &count) ); VERIFY_COND("count", (1 == count)); { const char* psz_my_key = NULL; const char* psz_my_val = NULL; SG_bool b; VERIFY_ERR_CHECK( SG_rbtree__iterator__first(pCtx, NULL, prb_reversed, &b, &psz_my_key, (void**) &psz_my_val) ); VERIFY_COND("ok", (0 == strcmp(psz_my_key, psz_hid_cs)) ); /* we don't know whether psz_my_val is tcrap or whatever. */ // VERIFY_COND("ok", (0 == strcmp(psz_my_val, "tcrap")) ); } SG_RBTREE_NULLFREE(pCtx, prb_reversed); SG_RBTREE_NULLFREE(pCtx, prb); { const char* psz_remove = "whatever"; VERIFY_ERR_CHECK( SG_vc_tags__remove(pCtx, pRepo, &q, 1, &psz_remove) ); /* Note that by removing whatever, we are bringing the tags list back * to a state where it has been before (just tcrap). This changeset in * the tags table will have its own csid, because the parentage is * different, but it's root idtrie HID will be the same as a previous * node. */ } VERIFY_ERR_CHECK( SG_vc_tags__lookup(pCtx, pRepo, psz_hid_cs, &pva) ); VERIFY_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); VERIFY_COND("count", (1 == count)); SG_VARRAY_NULLFREE(pCtx, pva); VERIFY_ERR_CHECK( SG_vc_tags__list(pCtx, pRepo, &prb) ); VERIFY_ERR_CHECK( SG_rbtree__count(pCtx, prb, &count) ); VERIFY_COND("count", (1 == count)); SG_RBTREE_NULLFREE(pCtx, prb); SG_REPO_NULLFREE(pCtx, pRepo); SG_DAGNODE_NULLFREE(pCtx, pdn); SG_PATHNAME_NULLFREE(pCtx, pPathWorkingDir); SG_PATHNAME_NULLFREE(pCtx, pPathFile); return 1; fail: SG_VHASH_NULLFREE(pCtx, pvh); SG_PATHNAME_NULLFREE(pCtx, pPathWorkingDir); SG_PATHNAME_NULLFREE(pCtx, pPathFile); return 0; }
void SG_vc_hooks__BROADCAST__AFTER_COMMIT( SG_context* pCtx, SG_repo* pRepo, SG_changeset* pcs, const char* psz_tied_branch_name, const SG_audit* pq, const char* psz_comment, const char* const* paszAssocs, SG_uint32 count_assocs, const SG_stringarray* psa_stamps ) { SG_varray* pva_hooks = NULL; SG_vhash* pvh_params = NULL; char* psz_repo_id = NULL; char* psz_admin_id = NULL; SG_ERR_CHECK( SG_vc_hooks__lookup_by_interface( pCtx, pRepo, SG_VC_HOOK__INTERFACE__BROADCAST__AFTER_COMMIT, &pva_hooks ) ); if (pva_hooks) { SG_uint32 count_hooks = 0; SG_uint32 i_hook = 0; const char* psz_descriptor_name = NULL; SG_ERR_CHECK( SG_repo__get_admin_id(pCtx, pRepo, &psz_admin_id) ); SG_ERR_CHECK( SG_repo__get_repo_id( pCtx, pRepo, &psz_repo_id ) ); SG_ERR_CHECK( SG_repo__get_descriptor_name(pCtx, pRepo, &psz_descriptor_name) ); SG_ERR_CHECK( SG_varray__count(pCtx, pva_hooks, &count_hooks) ); for (i_hook=0; i_hook<count_hooks; i_hook++) { SG_vhash* pvh_hook = NULL; const char* psz_js = NULL; const char* psz_csid = NULL; SG_vhash* pvh_changeset = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pva_hooks, i_hook, &pvh_hook) ); SG_ERR_CHECK( SG_changeset__get_id_ref(pCtx, pcs, &psz_csid) ); SG_ERR_CHECK( SG_changeset__get_vhash_ref(pCtx, pcs, &pvh_changeset) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh_hook, "js", &psz_js) ); SG_ERR_CHECK( SG_VHASH__ALLOC(pCtx, &pvh_params) ); SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "csid", psz_csid) ); SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "repo_id", psz_repo_id) ); SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "admin_id", psz_admin_id) ); if (psz_descriptor_name) { SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "descriptor_name", psz_descriptor_name) ); } if (pq) { SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "userid", pq->who_szUserId) ); } if (psz_comment) { SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "comment", psz_comment) ); } if (psz_tied_branch_name) { SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "branch", psz_tied_branch_name) ); } SG_ERR_CHECK( SG_vhash__addcopy__vhash(pCtx, pvh_params, "changeset", pvh_changeset) ); if (paszAssocs && count_assocs) { SG_uint32 i = 0; SG_varray* pva_ids = NULL; SG_ERR_CHECK( SG_vhash__addnew__varray(pCtx, pvh_params, "wit_ids", &pva_ids) ); for (i=0; i<count_assocs; i++) { SG_ERR_CHECK( SG_varray__append__string__sz(pCtx, pva_ids, paszAssocs[i]) ); } } if (psa_stamps) { SG_uint32 count = 0; SG_uint32 i = 0; SG_varray* pva_stamps = NULL; SG_ERR_CHECK( SG_vhash__addnew__varray(pCtx, pvh_params, "stamps", &pva_stamps) ); SG_ERR_CHECK( SG_stringarray__count(pCtx, psa_stamps, &count) ); for (i=0; i<count; i++) { const char* psz_stamp = NULL; SG_ERR_CHECK( SG_stringarray__get_nth(pCtx, psa_stamps, i, &psz_stamp) ); SG_ERR_CHECK( SG_varray__append__string__sz(pCtx, pva_stamps, psz_stamp) ); } } SG_ERR_CHECK( SG_vc_hooks__execute(pCtx, psz_js, pvh_params, NULL) ); SG_VHASH_NULLFREE(pCtx, pvh_params); } } fail: SG_VHASH_NULLFREE(pCtx, pvh_params); SG_VARRAY_NULLFREE(pCtx, pva_hooks); SG_NULLFREE(pCtx, psz_repo_id); SG_NULLFREE(pCtx, psz_admin_id); }
void SG_vc_hooks__install( SG_context* pCtx, SG_repo* pRepo, const char* psz_interface, const char* psz_js, const char *module, SG_uint32 version, SG_bool replaceOld, const SG_audit* pq ) { char* psz_hid_cs_leaf = NULL; SG_zingtx* pztx = NULL; SG_zingrecord* prec = NULL; SG_dagnode* pdn = NULL; SG_changeset* pcs = NULL; SG_zingtemplate* pzt = NULL; SG_zingfieldattributes* pzfa = NULL; SG_varray *oldRecs = NULL; SG_UNUSED(module); SG_UNUSED(version); if (replaceOld) { SG_ERR_CHECK( SG_vc_hooks__lookup_by_interface(pCtx, pRepo, psz_interface, &oldRecs) ); } // TODO consider validating the JS by compiling it SG_ERR_CHECK( SG_zing__get_leaf(pCtx, pRepo, NULL, SG_DAGNUM__VC_HOOKS, &psz_hid_cs_leaf) ); /* start a changeset */ SG_ERR_CHECK( SG_zing__begin_tx(pCtx, pRepo, SG_DAGNUM__VC_HOOKS, pq->who_szUserId, psz_hid_cs_leaf, &pztx) ); SG_ERR_CHECK( SG_zingtx__add_parent(pCtx, pztx, psz_hid_cs_leaf) ); if (replaceOld) { SG_uint32 i, count; SG_ERR_CHECK( SG_varray__count(pCtx, oldRecs, &count) ); for ( i = 0; i < count; ++i ) { const char *hidrec = NULL; SG_vhash *rec = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, oldRecs, i, &rec) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, rec, "hidrec", &hidrec) ); SG_ERR_CHECK( SG_zingtx__delete_record__hid(pCtx, pztx, "hook", hidrec) ); } } SG_ERR_CHECK( SG_zingtx__get_template(pCtx, pztx, &pzt) ); SG_ERR_CHECK( SG_zingtx__create_new_record(pCtx, pztx, "hook", &prec) ); SG_ERR_CHECK( SG_zingtemplate__get_field_attributes(pCtx, pzt, "hook", "interface", &pzfa) ); SG_ERR_CHECK( SG_zingrecord__set_field__string(pCtx, prec, pzfa, psz_interface) ); SG_ERR_CHECK( SG_zingtemplate__get_field_attributes(pCtx, pzt, "hook", "js", &pzfa) ); SG_ERR_CHECK( SG_zingrecord__set_field__string(pCtx, prec, pzfa, psz_js) ); if (module) { SG_ERR_CHECK( SG_zingtemplate__get_field_attributes(pCtx, pzt, "hook", "module", &pzfa) ); SG_ERR_CHECK( SG_zingrecord__set_field__string(pCtx, prec, pzfa, module) ); } if (version) { SG_ERR_CHECK( SG_zingtemplate__get_field_attributes(pCtx, pzt, "hook", "version", &pzfa) ); SG_ERR_CHECK( SG_zingrecord__set_field__int(pCtx, prec, pzfa, (SG_int64)version) ); } /* commit the changes */ SG_ERR_CHECK( SG_zing__commit_tx(pCtx, pq->when_int64, &pztx, &pcs, &pdn, NULL) ); // fall thru fail: if (pztx) { SG_ERR_IGNORE( SG_zing__abort_tx(pCtx, &pztx) ); } SG_VARRAY_NULLFREE(pCtx, oldRecs); SG_NULLFREE(pCtx, psz_hid_cs_leaf); SG_DAGNODE_NULLFREE(pCtx, pdn); SG_CHANGESET_NULLFREE(pCtx, pcs); }
void sg_vc_hooks__lookup_by_interface__single_result( SG_context* pCtx, SG_repo* pRepo, const char* psz_interface, SG_vhash** ppvh_latest_version ) { //This version will return only the hook with the largest version. //If multiple versions of the hook are defined, //all old versions will be deleted. SG_varray* pva_hooks = NULL; SG_vhash* pvh_latest_hook = NULL; SG_zingtx* pztx = NULL; char* psz_hid_cs_leaf = NULL; SG_dagnode* pdn = NULL; SG_changeset* pcs = NULL; SG_ERR_CHECK( SG_vc_hooks__lookup_by_interface( pCtx, pRepo, psz_interface, &pva_hooks ) ); if (!pva_hooks) { SG_ERR_THROW2( SG_ERR_VC_HOOK_MISSING, (pCtx, "%s", psz_interface) ); } else { SG_uint32 count = 0; SG_ERR_CHECK( SG_varray__count(pCtx, pva_hooks, &count) ); if (0 == count) { SG_ERR_THROW2( SG_ERR_VC_HOOK_MISSING, (pCtx, "%s", psz_interface) ); } else { if (count > 1) { SG_uint32 i = 0; SG_int32 nVersion = 0; SG_int32 nHighestVersion = -1; SG_int32 nAmbiguousVersion = -2; const char * hidRecToSave = NULL; const char * hidrec = NULL; SG_vhash * pvh_current_hook = NULL; //There are multiple versions installed for this hook. //delete the lesser numbered versions. for (i=0; i < count; i++) { SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pva_hooks, i, &pvh_current_hook) ); SG_ERR_CHECK( SG_vhash__get__int32(pCtx, pvh_current_hook, "version", &nVersion) ); if (nVersion == nHighestVersion) { nAmbiguousVersion = nHighestVersion; } if (nVersion > nHighestVersion) { nHighestVersion = nVersion; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh_current_hook, "hidrec", &hidRecToSave) ); } } if (nAmbiguousVersion == nHighestVersion) SG_ERR_THROW2( SG_ERR_VC_HOOK_AMBIGUOUS, (pCtx, "%s defined multiple times at version %d", psz_interface, nHighestVersion) ); if (nHighestVersion > 0 && hidRecToSave != NULL) { SG_audit q; SG_ERR_CHECK( SG_audit__init(pCtx,&q,pRepo,SG_AUDIT__WHEN__NOW,SG_AUDIT__WHO__FROM_SETTINGS) ); SG_ERR_CHECK( SG_zing__get_leaf(pCtx, pRepo, NULL, SG_DAGNUM__VC_HOOKS, &psz_hid_cs_leaf) ); /* start a changeset */ SG_ERR_CHECK( SG_zing__begin_tx(pCtx, pRepo, SG_DAGNUM__VC_HOOKS, q.who_szUserId, psz_hid_cs_leaf, &pztx) ); SG_ERR_CHECK( SG_zingtx__add_parent(pCtx, pztx, psz_hid_cs_leaf) ); for (i=0; i < count; i++) { SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pva_hooks, i, &pvh_current_hook) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh_current_hook, "hidrec", &hidrec) ); if (SG_strcmp__null(hidrec, hidRecToSave) != 0) { //This isn't the recid to save. Delete it! SG_ERR_CHECK( SG_zingtx__delete_record__hid(pCtx, pztx, "hook", hidrec) ); } } /* commit the changes */ SG_ERR_CHECK( SG_zing__commit_tx(pCtx, q.when_int64, &pztx, &pcs, &pdn, NULL) ); } } else { //There's only one hook installed return it. SG_vhash * pvh_temp = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pva_hooks, 0, &pvh_temp) ); SG_ERR_CHECK( SG_VHASH__ALLOC__COPY(pCtx, &pvh_latest_hook, pvh_temp) ); } } } SG_RETURN_AND_NULL(pvh_latest_hook, ppvh_latest_version); fail: if (pztx) { SG_ERR_IGNORE( SG_zing__abort_tx(pCtx, &pztx) ); } SG_NULLFREE(pCtx, psz_hid_cs_leaf); SG_DAGNODE_NULLFREE(pCtx, pdn); SG_CHANGESET_NULLFREE(pCtx, pcs); SG_VARRAY_NULLFREE(pCtx, pva_hooks); }
static void _fillInUserSelection( SG_context *pCtx, SG_string *pstrRepoDescriptorName, SG_string *replacement) { SG_repo *repo = NULL; SG_varray *users = NULL; SG_vhash *user = NULL; SG_uint32 i = 0; SG_uint32 count; SG_string *semail = NULL; SG_string *suid = NULL; SG_string *entry = NULL; SG_string *curuid = NULL; SG_ERR_CHECK( SG_string__clear(pCtx, replacement) ); SG_ERR_CHECK( SG_repo__open_repo_instance(pCtx, SG_string__sz(pstrRepoDescriptorName), &repo) ); SG_ERR_CHECK( SG_user__list_all(pCtx, repo, &users) ); SG_ERR_CHECK( SG_varray__count(pCtx, users, &count) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &semail) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &suid) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &entry) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &curuid) ); SG_ERR_CHECK( _getUserId(pCtx, repo, curuid) ); for ( i = 0; i < count; ++i ) { const char *uid = NULL; const char *email = NULL; const char *selected = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, users, i, &user) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, user, "recid", &uid) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, user, "email", &email) ); SG_ERR_CHECK( _getEncoded(pCtx, uid, suid) ); SG_ERR_CHECK( _getEncoded(pCtx, email, semail) ); if (eq(SG_string__sz(curuid), uid)) { selected = " selected='selected' "; } else { selected = ""; } SG_ERR_CHECK( SG_string__sprintf(pCtx, entry, "<option value=\"%s\" %s>%s</option>", SG_string__sz(suid), selected, SG_string__sz(semail)) ); SG_ERR_CHECK( SG_string__append__string(pCtx, replacement, entry) ); } fail: SG_VARRAY_NULLFREE(pCtx, users); SG_REPO_NULLFREE(pCtx, repo); SG_STRING_NULLFREE(pCtx, semail); SG_STRING_NULLFREE(pCtx, suid); SG_STRING_NULLFREE(pCtx, entry); SG_STRING_NULLFREE(pCtx, curuid); }