void SG_curl__set_headers_from_varray(SG_context * pCtx, SG_curl * pCurl, SG_varray * pvaHeaders, struct curl_slist ** ppHeaderList) { CURLcode rc = CURLE_OK; _sg_curl* p = (_sg_curl*)pCurl; struct curl_slist* pHeaderList = NULL; SG_uint32 count = 0; SG_uint32 i = 0; SG_NULLARGCHECK_RETURN(pCurl); SG_NULLARGCHECK_RETURN(pvaHeaders); SG_ERR_CHECK( SG_varray__count(pCtx, pvaHeaders, &count) ); for (i = 0; i < count; i++) { const char * psz = NULL; SG_ERR_CHECK_RETURN( SG_varray__get__sz(pCtx, pvaHeaders, i, &psz) ); pHeaderList = curl_slist_append(pHeaderList, psz); if (!pHeaderList) SG_ERR_THROW2_RETURN(SG_ERR_UNSPECIFIED, (pCtx, "Failed to add HTTP header.")); } rc = curl_easy_setopt(p->pCurl, CURLOPT_HTTPHEADER, pHeaderList); if (rc) SG_ERR_THROW2(SG_ERR_LIBCURL(rc), (pCtx, "Problem setting HTTP headers" )); SG_RETURN_AND_NULL(pHeaderList, ppHeaderList); fail: if (pHeaderList) SG_CURL_HEADERS_NULLFREE(pCtx, pHeaderList); }
void SG_vector_i64__alloc__from_varray(SG_context* pCtx, SG_varray* pva, SG_vector_i64 ** ppVector) { SG_uint32 count = 0; SG_uint32 i = 0; SG_vector_i64* pvec = NULL; SG_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); SG_ERR_CHECK( SG_vector_i64__alloc(pCtx, &pvec, count) ); for (i=0; i<count; i++) { SG_int64 v = 0; SG_ERR_CHECK( SG_varray__get__int64(pCtx, pva, i, &v) ); SG_ERR_CHECK( SG_vector_i64__append(pCtx, pvec, v, NULL) ); } *ppVector = pvec; pvec = NULL; return; fail: SG_VECTOR_I64_NULLFREE(pCtx, pvec); 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); }
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; }
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: ; }
/** * Take a domain-specific/relative repo-path and get * the GID ALIAS of the item. * * THIS IS STRICLTY BASED UPON THE FIXED CHANGESET * THAT WE ALREADY HAVE IN THE tne_* TABLE. It does * not know about or account for any pending changes * in the WD; that is, it DOES NOT know about tbl_PC. * * We DO NOT know if the given domain is appropriate * for the given pCSetRow. That is upto the caller. * For example, we expect them to map: * 'b' ==> "L0" * 'c' ==> "L1" * but we don't enforce that here. * */ void sg_wc_db__tne__get_alias_from_extended_repo_path(SG_context * pCtx, sg_wc_db * pDb, const sg_wc_db__cset_row * pCSetRow, const char * pszBaselineRepoPath, SG_bool * pbFound, SG_uint64 * puiAliasGid) { SG_varray * pva = NULL; sg_wc_db__tne_row * pTneRow_k = NULL; SG_uint64 uiAlias; SG_uint32 k, count; *pbFound = SG_FALSE; *puiAliasGid = 0; // TODO 2012/01/04 For now, require that an extended-prefix be // TODO present in the repo-path. // TODO // TODO We may relax this to allow a '/' current/live // TODO domain repo-path eventually. SG_ASSERT_RELEASE_FAIL( ((pszBaselineRepoPath[0]=='@') && (pszBaselineRepoPath[1]!='/')) ); SG_ERR_CHECK( SG_repopath__split_into_varray(pCtx, pszBaselineRepoPath, &pva) ); // the root directory should be "@b" and be contained in pva[0]. // we have a direct line to its alias. SG_ERR_CHECK( sg_wc_db__tne__get_alias_of_root(pCtx, pDb, pCSetRow, &uiAlias) ); SG_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); for (k=1; k<count; k++) { const char * pszEntryname_k; SG_bool bFound_k; SG_ERR_CHECK( SG_varray__get__sz(pCtx, pva, k, &pszEntryname_k) ); SG_ERR_CHECK( sg_wc_db__tne__get_row_by_parent_alias_and_entryname(pCtx, pDb, pCSetRow, uiAlias, pszEntryname_k, &bFound_k, &pTneRow_k) ); if (!bFound_k) goto fail; uiAlias = pTneRow_k->p_s->uiAliasGid; SG_WC_DB__TNE_ROW__NULLFREE(pCtx, pTneRow_k); } *pbFound = SG_TRUE; *puiAliasGid = uiAlias; fail: SG_VARRAY_NULLFREE(pCtx, pva); SG_WC_DB__TNE_ROW__NULLFREE(pCtx, pTneRow_k); }
void SG_localsettings__varray__remove_first_match(SG_context * pCtx, const char* psz_path, const char* psz_val) { SG_jsondb* p = NULL; SG_string* pstr_path_element = NULL; SG_varray* pva = NULL; SG_uint32 ndx = 0; SG_uint32 count = 0; SG_uint32 i = 0; SG_bool b_found = SG_FALSE; SG_string* pstr_path_found = NULL; SG_ASSERT(pCtx); SG_NONEMPTYCHECK_RETURN(psz_path); SG_ERR_CHECK( SG_closet__get_localsettings(pCtx, &p) ); SG_ERR_CHECK( SG_localsettings__get__varray(pCtx, psz_path, NULL, &pva, &pstr_path_found) ); if (pva) { if (!pstr_path_found) { // this came from factory defaults. SG_ERR_CHECK( SG_string__alloc(pCtx, &pstr_path_found) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path_found, "%s/%s", SG_LOCALSETTING__SCOPE__MACHINE, psz_path) ); SG_ERR_CHECK( SG_localsettings__update__varray(pCtx, SG_string__sz(pstr_path_found), pva) ); } SG_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); for (i=0; i<count; i++) { const char* psz = NULL; SG_ERR_CHECK( SG_varray__get__sz(pCtx, pva, i, &psz) ); if (0 == strcmp(psz, psz_val)) { b_found = SG_TRUE; ndx = i; break; } } if (b_found) { SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr_path_element) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path_element, "%s/%d", SG_string__sz(pstr_path_found), ndx) ); SG_ERR_CHECK( SG_jsondb__remove(pCtx, p, SG_string__sz(pstr_path_element)) ); } } fail: SG_VARRAY_NULLFREE(pCtx, pva); SG_STRING_NULLFREE(pCtx, pstr_path_found); SG_STRING_NULLFREE(pCtx, pstr_path_element); SG_JSONDB_NULLFREE(pCtx, p); }
/** * 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); }
static void old_SG_db__make_delta_from_path( SG_context* pCtx, SG_repo* pRepo, SG_uint64 dagnum, SG_varray* pva_path, SG_vhash* pvh_add, SG_vhash* pvh_remove ) { SG_uint32 count = 0; SG_uint32 i_path_step = 0; SG_uint32 count_ops = 0; SG_UNUSED(dagnum); SG_NULLARGCHECK_RETURN(pRepo); SG_ERR_CHECK( SG_varray__count(pCtx, pva_path, &count) ); SG_ASSERT(count >= 2); SG_ERR_CHECK( SG_log__push_operation(pCtx, "Collecting deltas", SG_LOG__FLAG__NONE) ); count_ops++; SG_ERR_CHECK( SG_log__set_steps(pCtx, count-1, "changesets") ); for (i_path_step=0; i_path_step<(count-1); i_path_step++) { SG_ERR_CHECK( loop_innards_make_delta(pCtx, pRepo, pva_path, i_path_step, pvh_add, pvh_remove) ); SG_ERR_CHECK( SG_log__finish_step(pCtx) ); } SG_ERR_CHECK( SG_log__pop_operation(pCtx) ); count_ops--; fail: while (count_ops) { SG_ERR_IGNORE( SG_log__pop_operation(pCtx) ); count_ops--; } }
/** * 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; }
void SG_tag__add_tags(SG_context * pCtx, SG_repo * pRepo, SG_pendingtree * pPendingTree, const char* psz_spec_cs, SG_bool bRev, SG_bool bForce, const char** ppszTags, SG_uint32 count_args) { SG_pathname* pPathCwd = NULL; char* psz_hid_cs = NULL; SG_audit q; SG_uint32 i = 0; char * psz_current_hid_with_that_tag = NULL; SG_bool bFreePendingTree = SG_FALSE; SG_ERR_CHECK( SG_audit__init(pCtx,&q,pRepo,SG_AUDIT__WHEN__NOW,SG_AUDIT__WHO__FROM_SETTINGS) ); // TODO 4/21/10 pendingtree contains a pRepo inside it. we should // TODO 4/21/10 refactor this to alloc the pendingtree first and then // TODO 4/21/10 just borrow the pRepo from it. if (psz_spec_cs) { if (bRev) { SG_ERR_CHECK( SG_repo__hidlookup__dagnode(pCtx, pRepo, SG_DAGNUM__VERSION_CONTROL, psz_spec_cs, &psz_hid_cs) ); } else { SG_ERR_CHECK( SG_vc_tags__lookup__tag(pCtx, pRepo, psz_spec_cs, &psz_hid_cs) ); if (psz_hid_cs == NULL) SG_ERR_THROW(SG_ERR_TAG_NOT_FOUND); } } else { // tag the current baseline. // // when we have an uncomitted merge, we will have more than one parent. // what does this command mean then? It feels like we we should throw // an error and say that you have to commit first. const SG_varray * pva_wd_parents; // we do not own this const char * psz_hid_parent_0; // we do not own this SG_uint32 nrParents; if (pPendingTree == NULL) { SG_ERR_CHECK( SG_pendingtree__alloc_from_cwd(pCtx, SG_TRUE, &pPendingTree) ); bFreePendingTree = SG_TRUE; } SG_ERR_CHECK( SG_pendingtree__get_wd_parents__ref(pCtx, pPendingTree, &pva_wd_parents) ); SG_ERR_CHECK( SG_varray__count(pCtx, pva_wd_parents, &nrParents) ); if (nrParents > 1) SG_ERR_THROW( SG_ERR_CANNOT_DO_WHILE_UNCOMMITTED_MERGE ); SG_ERR_CHECK( SG_varray__get__sz(pCtx, pva_wd_parents, 0, &psz_hid_parent_0) ); SG_ERR_CHECK( SG_strdup(pCtx, psz_hid_parent_0, &psz_hid_cs) ); } if (!bForce) { //Go through and check all tags to make sure that they are not already applied. for (i = 0; i < count_args; i++) { const char * pszTag = ppszTags[i]; SG_ERR_IGNORE( SG_vc_tags__lookup__tag(pCtx, pRepo, pszTag, &psz_current_hid_with_that_tag) ); if (psz_current_hid_with_that_tag != NULL && 0 != strcmp(psz_current_hid_with_that_tag, psz_hid_cs)) //The tag has been applied, but not to the given changeset. SG_ERR_THROW(SG_ERR_TAG_ALREADY_EXISTS); SG_NULLFREE(pCtx, psz_current_hid_with_that_tag); } } for (i = 0; i < count_args; i++) { const char * pszTag = ppszTags[i]; SG_ERR_CHECK( SG_vc_tags__lookup__tag(pCtx, pRepo, pszTag, &psz_current_hid_with_that_tag) ); if (psz_current_hid_with_that_tag == NULL || 0 != strcmp(psz_current_hid_with_that_tag, psz_hid_cs)) { //The tag has not been applied, or it's been applied to a different dagnode. if ( psz_current_hid_with_that_tag != NULL && bForce) //Remove it, if it's already there SG_ERR_CHECK( SG_vc_tags__remove(pCtx, pRepo, &q, 1, &pszTag) ); SG_ERR_CHECK( SG_vc_tags__add(pCtx, pRepo, psz_hid_cs, pszTag, &q) ); } SG_NULLFREE(pCtx, psz_current_hid_with_that_tag); } fail: SG_NULLFREE(pCtx, psz_current_hid_with_that_tag); if (bFreePendingTree == SG_TRUE) SG_PENDINGTREE_NULLFREE(pCtx, pPendingTree); SG_NULLFREE(pCtx, psz_hid_cs); SG_PATHNAME_NULLFREE(pCtx, pPathCwd); }
// Helper function for _tree__init__continuation() static void _tree__add_subtoken(SG_context * pCtx, _tree_t * pTree, _node_t * pParent, SG_varray * pSubtoken) { SG_int64 revno = 0; char * szHid = NULL; _node_t * pNodeRef = NULL; SG_uint32 count = 0; SG_uint32 i = 0; SG_uint16 lastElementType = 0; SG_varray * pSubsubtoken = NULL; SG_stringarray * psa = NULL; ++pTree->indentLevel; SG_ERR_CHECK( SG_varray__get__int64(pCtx, pSubtoken, 0, &revno) ); SG_ERR_CHECK( SG_repo__find_dagnode_by_rev_id(pCtx, pTree->pRepoRef, SG_DAGNUM__VERSION_CONTROL, (SG_uint32)revno, &szHid) ); SG_ERR_CHECK( _tree__add_new_node(pCtx, pTree, pParent, szHid, &pNodeRef) ); pNodeRef->isPending = SG_FALSE; SG_NULLFREE(pCtx, szHid); SG_ERR_CHECK( SG_varray__count(pCtx, pSubtoken, &count) ); SG_ERR_CHECK( SG_varray__typeof(pCtx, pSubtoken, count-1, &lastElementType) ); if(lastElementType==SG_VARIANT_TYPE_VARRAY) { SG_ERR_CHECK( SG_varray__get__varray(pCtx, pSubtoken, count-1, &pSubsubtoken) ); --count; } for(i=1; i<count; ++i) { _node_t * pRefChildNode = NULL; SG_ERR_CHECK( SG_varray__get__int64(pCtx, pSubtoken, i, &revno) ); if(i==1 && revno==0) { // Revno 0 in the as the first child means "Use the LCA for the baseline". SG_uint32 j = 0; if(pSubsubtoken!=NULL) { // LCA would not include nodes in the sub-token. Just error out. SG_ERR_THROW(SG_ERR_INVALIDARG); } if(count-2<2) { // This would be interpreted as merging a node with itself or with nothing. SG_ERR_THROW(SG_ERR_INVALIDARG); } SG_ERR_CHECK( SG_STRINGARRAY__ALLOC(pCtx, &psa, count-2) ); for(j=2; j<count; ++j) { SG_ERR_CHECK( SG_varray__get__int64(pCtx, pSubtoken, j, &revno) ); SG_ERR_CHECK( SG_repo__find_dagnode_by_rev_id(pCtx, pTree->pRepoRef, SG_DAGNUM__VERSION_CONTROL, (SG_uint32)revno, &szHid) ); SG_ERR_CHECK( SG_stringarray__add(pCtx, psa, szHid) ); SG_NULLFREE(pCtx, szHid); } SG_ERR_CHECK( SG_dagquery__highest_revno_common_ancestor(pCtx, pTree->pRepoRef, SG_DAGNUM__VERSION_CONTROL, psa, &szHid) ); SG_STRINGARRAY_NULLFREE(pCtx, psa); } else { SG_ERR_CHECK( SG_repo__find_dagnode_by_rev_id(pCtx, pTree->pRepoRef, SG_DAGNUM__VERSION_CONTROL, (SG_uint32)revno, &szHid) ); } SG_ERR_CHECK( _tree__add_new_node(pCtx, pTree, pNodeRef, szHid, &pRefChildNode) ); pTree->pNextResult = pRefChildNode; SG_ERR_CHECK( _node_list__append(pCtx, &pTree->pending, &pRefChildNode) ); SG_NULLFREE(pCtx, szHid); } if(pSubsubtoken!=NULL) { SG_ERR_CHECK( _tree__add_subtoken(pCtx, pTree, pNodeRef, pSubsubtoken) ); } return; fail: SG_NULLFREE(pCtx, szHid); SG_STRINGARRAY_NULLFREE(pCtx, psa); }
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_pack__do_changeset(SG_context* pCtx, SG_repo* pRepo, const char* psz_hid_cs, SG_rbtree* prb_blobs) { SG_changeset* pcs = NULL; SG_int32 gen = 0; SG_uint32 count_blobs = 0; SG_uint32 count_parents = 0; SG_varray* pva_parents = NULL; SG_uint32 i; SG_rbtree* prb_new = NULL; const char* psz_hid_root_treenode = NULL; const char* psz_key = NULL; SG_vhash* pvh_lbl = NULL; SG_vhash* pvh_blobs = NULL; SG_ERR_CHECK( SG_changeset__load_from_repo(pCtx, pRepo, psz_hid_cs, &pcs) ); SG_ERR_CHECK( SG_changeset__get_root(pCtx, pcs, &psz_hid_root_treenode) ); SG_ERR_CHECK( SG_changeset__get_generation(pCtx, pcs, &gen) ); SG_ERR_CHECK( SG_RBTREE__ALLOC__PARAMS(pCtx, &prb_new, count_blobs, NULL) ); SG_ERR_CHECK( SG_changeset__get_list_of_bloblists(pCtx, pcs, &pvh_lbl) ); /* add all the tree user file blobs */ SG_ERR_CHECK( SG_changeset__get_bloblist_name(pCtx, SG_BLOB_REFTYPE__TREEUSERFILE, &psz_key) ); SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvh_lbl, psz_key, &pvh_blobs) ); SG_ERR_CHECK( SG_vhash__count(pCtx, pvh_blobs, &count_blobs) ); /* now write all the blobs */ for (i=0; i<count_blobs; i++) { const char* psz_hid = NULL; SG_ERR_CHECK( SG_vhash__get_nth_pair(pCtx, pvh_blobs, i, &psz_hid, NULL) ); SG_ERR_CHECK( SG_rbtree__add(pCtx, prb_new, psz_hid) ); } /* and the treenode blobs */ SG_ERR_CHECK( SG_changeset__get_bloblist_name(pCtx, SG_BLOB_REFTYPE__TREENODE, &psz_key) ); SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvh_lbl, psz_key, &pvh_blobs) ); SG_ERR_CHECK( SG_vhash__count(pCtx, pvh_blobs, &count_blobs) ); /* now write all the blobs */ for (i=0; i<count_blobs; i++) { const char* psz_hid = NULL; SG_ERR_CHECK( SG_rbtree__add(pCtx, prb_new, psz_hid) ); } SG_ERR_CHECK( sg_pack__do_get_dir__top(pCtx, pRepo, gen, psz_hid_root_treenode, prb_blobs, prb_new) ); SG_RBTREE_NULLFREE(pCtx, prb_new); SG_ERR_CHECK( SG_changeset__get_parents(pCtx, pcs, &pva_parents) ); if (pva_parents) { SG_ERR_CHECK( SG_varray__count(pCtx, pva_parents, &count_parents) ); for (i=0; i<count_parents; i++) { const char* psz_hid = NULL; SG_ERR_CHECK( SG_varray__get__sz(pCtx, pva_parents, i, &psz_hid) ); SG_ERR_CHECK( sg_pack__do_changeset(pCtx, pRepo, psz_hid, prb_blobs) ); } } SG_CHANGESET_NULLFREE(pCtx, pcs); return; fail: SG_RBTREE_NULLFREE(pCtx, prb_new); }
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); }
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); }
static void SG_db__make_delta_from_path( SG_context* pCtx, SG_repo* pRepo, SG_uint64 dagnum, SG_varray* pva_path, SG_uint32 flags, SG_vhash* pvh_add, SG_vhash* pvh_remove ) { #if SG_DOUBLE_CHECK__CALC_DELTA SG_int64 t1 = -1; SG_int64 t2 = -1; SG_vhash* new_pvh_add = NULL; SG_vhash* new_pvh_remove = NULL; SG_vhash* old_pvh_add = NULL; SG_vhash* old_pvh_remove = NULL; SG_string* old_pstr = NULL; SG_string* new_pstr = NULL; SG_ERR_CHECK( SG_vhash__alloc__copy(pCtx, &new_pvh_add, pvh_add) ); SG_ERR_CHECK( SG_vhash__alloc__copy(pCtx, &new_pvh_remove, pvh_remove) ); SG_ERR_CHECK( SG_vhash__alloc__copy(pCtx, &old_pvh_add, pvh_add) ); SG_ERR_CHECK( SG_vhash__alloc__copy(pCtx, &old_pvh_remove, pvh_remove) ); SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &t1) ); SG_ERR_CHECK( old_SG_db__make_delta_from_path( pCtx, pRepo, dagnum, pva_path, old_pvh_add, old_pvh_remove ) ); SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &t2) ); { SG_uint32 path_length = 0; SG_ERR_CHECK( SG_varray__count(pCtx, pva_path, &path_length) ); fprintf(stderr, "make_delta_from_path (%d)\n", path_length); } fprintf(stderr, " time old %d ms\n", (int) (t2 - t1)); SG_ERR_CHECK( SG_vhash__sort(pCtx, old_pvh_add, SG_FALSE, SG_vhash_sort_callback__increasing) ); SG_ERR_CHECK( SG_vhash__sort(pCtx, old_pvh_remove, SG_FALSE, SG_vhash_sort_callback__increasing) ); SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &t1) ); SG_ERR_CHECK( SG_repo__dbndx__make_delta_from_path( pCtx, pRepo, dagnum, pva_path, 0, new_pvh_add, new_pvh_remove ) ); SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &t2) ); fprintf(stderr, " time new %d ms\n", (int) (t2 - t1)); SG_ERR_CHECK( SG_vhash__sort(pCtx, new_pvh_add, SG_FALSE, SG_vhash_sort_callback__increasing) ); SG_ERR_CHECK( SG_vhash__sort(pCtx, new_pvh_remove, SG_FALSE, SG_vhash_sort_callback__increasing) ); SG_ERR_CHECK( SG_string__alloc(pCtx, &old_pstr) ); SG_ERR_CHECK( SG_vhash__to_json(pCtx, old_pvh_add, old_pstr) ); SG_ERR_CHECK( SG_string__alloc(pCtx, &new_pstr) ); SG_ERR_CHECK( SG_vhash__to_json(pCtx, new_pvh_add, new_pstr) ); if (0 != strcmp(SG_string__sz(old_pstr), SG_string__sz(new_pstr))) { fprintf(stderr, "oldway:\n"); SG_VHASH_STDERR(old_pvh_add); fprintf(stderr, "new:\n"); SG_VHASH_STDERR(new_pvh_add); SG_ERR_THROW( SG_ERR_UNSPECIFIED ); } SG_STRING_NULLFREE(pCtx, old_pstr); SG_STRING_NULLFREE(pCtx, new_pstr); SG_ERR_CHECK( SG_string__alloc(pCtx, &old_pstr) ); SG_ERR_CHECK( SG_vhash__to_json(pCtx, old_pvh_remove, old_pstr) ); SG_ERR_CHECK( SG_string__alloc(pCtx, &new_pstr) ); SG_ERR_CHECK( SG_vhash__to_json(pCtx, new_pvh_remove, new_pstr) ); if (0 != strcmp(SG_string__sz(old_pstr), SG_string__sz(new_pstr))) { fprintf(stderr, "oldway:\n"); SG_VHASH_STDERR(old_pvh_remove); fprintf(stderr, "new:\n"); SG_VHASH_STDERR(new_pvh_remove); SG_ERR_THROW( SG_ERR_UNSPECIFIED ); } #endif #if SG_DOUBLE_CHECK__CALC_DELTA SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &t1) ); #endif SG_ERR_CHECK( SG_repo__dbndx__make_delta_from_path( pCtx, pRepo, dagnum, pva_path, flags, pvh_add, pvh_remove ) ); #if SG_DOUBLE_CHECK__CALC_DELTA SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &t2) ); fprintf(stderr, " time NEW %d ms\n", (int) (t2 - t1)); #endif fail: #if SG_DOUBLE_CHECK__CALC_DELTA SG_STRING_NULLFREE(pCtx, old_pstr); SG_STRING_NULLFREE(pCtx, new_pstr); SG_VHASH_NULLFREE(pCtx, old_pvh_add); SG_VHASH_NULLFREE(pCtx, old_pvh_remove); SG_VHASH_NULLFREE(pCtx, new_pvh_add); SG_VHASH_NULLFREE(pCtx, new_pvh_remove); #endif ; }
/** * 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); }
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); }
void sg_vv2__history__working_folder( SG_context * pCtx, const SG_stringarray * psaInputs, const SG_rev_spec* pRevSpec, const SG_rev_spec* pRevSpec_single_revisions, const char* pszUser, const char* pszStamp, SG_bool bDetectCurrentBranch, SG_uint32 nResultLimit, SG_bool bHideObjectMerges, SG_int64 nFromDate, SG_int64 nToDate, SG_bool bListAll, SG_bool* pbHasResult, SG_vhash** ppvhBranchPile, SG_history_result ** ppResult, SG_history_token ** ppHistoryToken) { SG_repo * pRepo = NULL; SG_stringarray * pStringArrayGIDs = NULL; SG_stringarray * pStringArrayChangesets = NULL; SG_stringarray * pStringArrayChangesetsMissing = NULL; SG_stringarray * pStringArrayChangesets_single_revisions = NULL; SG_bool bRecommendDagWalk = SG_FALSE; SG_bool bLeaves = SG_FALSE; const char * pszBranchName = NULL; // we do not own this SG_vhash* pvhBranchPile = NULL; SG_varray* pvaParents = NULL; // we do not own this SG_bool bMyBranchWalkRecommendation = SG_FALSE; SG_rev_spec* pRevSpec_Allocated = NULL; SG_wc_tx * pWcTx = NULL; SG_vhash * pvhInfo = NULL; SG_uint32 count_args = 0; SG_uint32 countRevsSpecified = 0; if (psaInputs) SG_ERR_CHECK( SG_stringarray__count(pCtx, psaInputs, &count_args) ); // Use the WD to try to get the initial info. // I'm going to deviate from the model and use // a read-only TX here so that I can get a bunch // of fields that we need later. SG_ERR_CHECK( SG_WC_TX__ALLOC__BEGIN(pCtx, &pWcTx, NULL, SG_TRUE) ); if (count_args > 0) SG_ERR_CHECK( SG_wc_tx__get_item_gid__stringarray(pCtx, pWcTx, psaInputs, &pStringArrayGIDs) ); SG_ERR_CHECK( SG_wc_tx__get_wc_info(pCtx, pWcTx, &pvhInfo) ); SG_ERR_CHECK( SG_wc_tx__get_repo_and_wd_top(pCtx, pWcTx, &pRepo, NULL) ); /* If no revisions were specified, and the caller wants us to use the current branch, * create a revision spec with the current branch. */ if (pRevSpec) { SG_ERR_CHECK( SG_REV_SPEC__ALLOC__COPY(pCtx, pRevSpec, &pRevSpec_Allocated) ); SG_ERR_CHECK( SG_rev_spec__count(pCtx, pRevSpec_Allocated, &countRevsSpecified) ); } else { SG_ERR_CHECK( SG_REV_SPEC__ALLOC(pCtx, &pRevSpec_Allocated) ); } if (pRevSpec_single_revisions != NULL) { SG_uint32 countRevsSpecified_singles = 0; SG_ERR_CHECK( SG_rev_spec__count(pCtx, pRevSpec_single_revisions, &countRevsSpecified_singles) ); countRevsSpecified += countRevsSpecified_singles; } if (bDetectCurrentBranch && countRevsSpecified == 0) { SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pvhInfo, "branch", &pszBranchName) ); if (pszBranchName) { /* The working folder is attached to a branch. Does it exist? */ SG_bool bHasBranches = SG_FALSE; SG_bool bBranchExists = SG_FALSE; SG_ERR_CHECK( SG_vc_branches__cleanup(pCtx, pRepo, &pvhBranchPile) ); SG_ERR_CHECK( SG_vhash__has(pCtx, pvhBranchPile, "branches", &bHasBranches) ); if (bHasBranches) { SG_vhash* pvhRefBranches; SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhBranchPile, "branches", &pvhRefBranches) ); SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRefBranches, pszBranchName, &bBranchExists) ); } if (bBranchExists) { SG_uint32 numParents, i; const char* pszRefParent; /* If that branch exists, just add to our rev spec. */ SG_ERR_CHECK( SG_rev_spec__add_branch(pCtx, pRevSpec_Allocated, pszBranchName) ); /* Plus, if the working folder's parents are not in the branch (yet), add them as well * (they'll be in it after the user commits something...). */ SG_ERR_CHECK( SG_vhash__get__varray(pCtx, pvhInfo, "parents", &pvaParents) ); SG_ERR_CHECK( SG_varray__count(pCtx, pvaParents, &numParents) ); for (i = 0; i < numParents; i++) { SG_bool already_in_rev_spec = SG_FALSE; SG_ERR_CHECK( SG_varray__get__sz(pCtx, pvaParents, i, &pszRefParent) ); SG_ERR_CHECK( SG_rev_spec__contains(pCtx, pRepo, pRevSpec_Allocated, pszRefParent, &already_in_rev_spec) ); if(!already_in_rev_spec) SG_ERR_CHECK( SG_rev_spec__add_rev(pCtx, pRevSpec_Allocated, pszRefParent) ); } } else { /* If the branch doesn't exist, add the working folder's baseline(s) to the rev spec * and force a dag walk. */ SG_uint32 numParents, i; const char* pszRefParent; SG_ERR_CHECK( SG_vhash__get__varray(pCtx, pvhInfo, "parents", &pvaParents) ); SG_ERR_CHECK( SG_varray__count(pCtx, pvaParents, &numParents) ); for (i = 0; i < numParents; i++) { SG_ERR_CHECK( SG_varray__get__sz(pCtx, pvaParents, i, &pszRefParent) ); SG_ERR_CHECK( SG_rev_spec__add_rev(pCtx, pRevSpec_Allocated, pszRefParent) ); } bMyBranchWalkRecommendation = SG_TRUE; } } } // Determine the starting changeset IDs. strBranch and bLeaves control this. // We do this step here, so that repo paths can be looked up before we call into history__core. SG_ERR_CHECK( sg_vv2__history__get_starting_changesets(pCtx, pRepo, pRevSpec_Allocated, &pStringArrayChangesets, &pStringArrayChangesetsMissing, &bRecommendDagWalk, &bLeaves) ); if (pStringArrayChangesetsMissing) { // See K2177, K1322, W0836, W8132. We requested specific starting // points and ran into some csets that were referenced (by --tag // or --branch) that are not present in the local repo. Try to // silently ignore them. SG_uint32 nrFound = 0; SG_ERR_CHECK( SG_stringarray__count(pCtx, pStringArrayChangesets, &nrFound) ); if (nrFound > 0) { // Yes there were missing csets, but we still found some // of the referenced ones. Just ignore the missing ones. // This should behave just like we had the older tag/branch // dag prior to the push -r on the vc dag. } else { const char * psz_0; // TODO 2012/10/19 Do we want a different message if the number of missing is > 1 ? SG_ERR_CHECK( SG_stringarray__get_nth(pCtx, pStringArrayChangesetsMissing, 0, &psz_0) ); SG_ERR_THROW2( SG_ERR_CHANGESET_BLOB_NOT_FOUND, (pCtx, "%s", psz_0) ); } } bRecommendDagWalk = bRecommendDagWalk || bMyBranchWalkRecommendation; //This hack is here to detect when we're being asked for the parent of a certain //object from the sg_parents code. parents always wants the dag walk. //The better solution would be to allow users to pass in a flag about their dagwalk //preferences if (count_args == 1 && nResultLimit == 1) bRecommendDagWalk = SG_TRUE; if (bListAll) { // See W8493. If they gave us a --list-all along with a --rev or --tag, they // want to force us to show the full history rather than just the info for the // named cset. bRecommendDagWalk = SG_TRUE; } if (pRevSpec_single_revisions) { // We DO NOT pass a psaMissingHids here because we want // it to throw if the user names a missing cset. SG_ERR_CHECK( SG_rev_spec__get_all__repo__dedup(pCtx, pRepo, pRevSpec_single_revisions, SG_TRUE, &pStringArrayChangesets_single_revisions, NULL) ); } // TODO 2012/07/03 The deviates from the model. This call directly returns the // TODO allocated data into the caller's pointers. If anything fails // TODO (such as the call to get the branches below), we'll probably // TODO leak the result and token. SG_ERR_CHECK( SG_history__run(pCtx, pRepo, pStringArrayGIDs, pStringArrayChangesets, pStringArrayChangesets_single_revisions, pszUser, pszStamp, nResultLimit, bLeaves, bHideObjectMerges, nFromDate, nToDate, bRecommendDagWalk, SG_FALSE, pbHasResult, ppResult, ppHistoryToken) ); /* This is kind of a hack. History callers often need branch data to format ouput. * But we open the repo down here. I didn't want to open/close it again. And there's logic * in here about which repo to open. So instead, we do this. */ if (ppvhBranchPile) { if (pvhBranchPile) { *ppvhBranchPile = pvhBranchPile; pvhBranchPile = NULL; } else SG_ERR_CHECK( SG_vc_branches__cleanup(pCtx, pRepo, ppvhBranchPile) ); } fail: SG_ERR_IGNORE( SG_wc_tx__cancel(pCtx, pWcTx) ); SG_WC_TX__NULLFREE(pCtx, pWcTx); SG_REV_SPEC_NULLFREE(pCtx, pRevSpec_Allocated); SG_STRINGARRAY_NULLFREE(pCtx, pStringArrayChangesets); SG_STRINGARRAY_NULLFREE(pCtx, pStringArrayChangesetsMissing); SG_STRINGARRAY_NULLFREE(pCtx, pStringArrayChangesets_single_revisions); SG_STRINGARRAY_NULLFREE(pCtx, pStringArrayGIDs); SG_VHASH_NULLFREE(pCtx, pvhBranchPile); SG_VHASH_NULLFREE(pCtx, pvhInfo); SG_REPO_NULLFREE(pCtx, pRepo); }
/** * 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); }
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); }