static void _add_dagnodes_until_connected(SG_context* pCtx, SG_vhash** ppvhStagingStatus, sg_pull_instance_data* pMe, SG_client* pClient) { SG_bool disconnected = SG_FALSE; SG_vhash* pvhFragballRequest = NULL; char* pszFragballName = NULL; SG_vhash* pvhRequestStatus = NULL; const SG_pathname* pStagingPathname; SG_ERR_CHECK( SG_staging__get_pathname(pCtx, pMe->pStaging, &pStagingPathname) ); SG_ERR_CHECK( SG_vhash__has(pCtx, *ppvhStagingStatus, SG_SYNC_STATUS_KEY__DAGS, &disconnected) ); while (disconnected) { #if TRACE_PULL SG_ERR_CHECK( SG_vhash_debug__dump_to_console__named(pCtx, *ppvhStagingStatus, "pull staging status") ); #endif // There's at least one dag with connection problems. // Convert the staging status vhash into a fragball request vhash. pvhFragballRequest = *ppvhStagingStatus; *ppvhStagingStatus = NULL; SG_ERR_CHECK( SG_vhash__add__int64(pCtx, pvhFragballRequest, SG_SYNC_STATUS_KEY__GENERATIONS, GENERATIONS_PER_ROUNDTRIP) ); SG_ERR_CHECK( SG_client__pull_request_fragball(pCtx, pClient, pvhFragballRequest, pStagingPathname, &pszFragballName, &pvhRequestStatus) ); /* Ian TODO: inspect pvhRequestStatus */ SG_VHASH_NULLFREE(pCtx, pvhRequestStatus); SG_VHASH_NULLFREE(pCtx, pvhFragballRequest); SG_ERR_CHECK( SG_staging__slurp_fragball(pCtx, pMe->pStaging, (const char*)pszFragballName) ); SG_NULLFREE(pCtx, pszFragballName); SG_ERR_CHECK( SG_staging__check_status(pCtx, pMe->pStaging, SG_TRUE, SG_FALSE, SG_FALSE, SG_FALSE, SG_FALSE, ppvhStagingStatus) ); SG_ERR_CHECK( SG_vhash__has(pCtx, *ppvhStagingStatus, SG_SYNC_STATUS_KEY__DAGS, &disconnected) ); #if TRACE_PULL SG_ERR_CHECK( SG_vhash_debug__dump_to_console__named(pCtx, *ppvhStagingStatus, "pull staging status") ); #endif } SG_ERR_CHECK_RETURN( SG_context__msg__emit(pCtx, "done") ); /* fall through */ fail: SG_VHASH_NULLFREE(pCtx, *ppvhStagingStatus); SG_VHASH_NULLFREE(pCtx, pvhFragballRequest); SG_NULLFREE(pCtx, pszFragballName); SG_VHASH_NULLFREE(pCtx, pvhRequestStatus); SG_ERR_IGNORE( SG_context__msg__emit(pCtx, "\n") ); }
static void _add_blobs_until_done(SG_context* pCtx, SG_staging* pStaging, SG_client* pClient) { SG_bool need_blobs = SG_FALSE; SG_vhash* pvhFragballRequest = NULL; char* pszFragballName = NULL; SG_vhash* pvhRequestStatus = NULL; const SG_pathname* pStagingPathname = NULL; SG_vhash* pvhStagingStatus = NULL; SG_ERR_CHECK_RETURN( SG_context__msg__emit(pCtx, "Retrieving blobs...") ); SG_ERR_CHECK( SG_staging__check_status(pCtx, pStaging, SG_FALSE, SG_FALSE, SG_FALSE, SG_TRUE, SG_TRUE, &pvhStagingStatus) ); SG_ERR_CHECK( SG_vhash__has(pCtx, pvhStagingStatus, SG_SYNC_STATUS_KEY__BLOBS, &need_blobs) ); if (need_blobs) SG_ERR_CHECK( SG_staging__get_pathname(pCtx, pStaging, &pStagingPathname) ); while (need_blobs) { pvhFragballRequest = pvhStagingStatus; pvhStagingStatus = NULL; SG_ERR_CHECK( SG_client__pull_request_fragball(pCtx, pClient, pvhFragballRequest, pStagingPathname, &pszFragballName, &pvhRequestStatus) ); /* Ian TODO: inspect pvhRequestStatus */ SG_VHASH_NULLFREE(pCtx, pvhRequestStatus); SG_VHASH_NULLFREE(pCtx, pvhFragballRequest); SG_ERR_CHECK( SG_staging__slurp_fragball(pCtx, pStaging, (const char*)pszFragballName) ); SG_NULLFREE(pCtx, pszFragballName); SG_ERR_CHECK( SG_staging__check_status(pCtx, pStaging, SG_FALSE, SG_FALSE, SG_FALSE, SG_TRUE, SG_TRUE, &pvhStagingStatus) ); #if TRACE_PULL SG_ERR_CHECK( SG_vhash_debug__dump_to_console__named(pCtx, pvhStagingStatus, "pull staging status") ); #endif SG_ERR_CHECK( SG_vhash__has(pCtx, pvhStagingStatus, SG_SYNC_STATUS_KEY__BLOBS, &need_blobs) ); } SG_ERR_CHECK_RETURN( SG_context__msg__emit(pCtx, "done") ); /* fall through */ fail: SG_VHASH_NULLFREE(pCtx, pvhStagingStatus); SG_VHASH_NULLFREE(pCtx, pvhFragballRequest); SG_NULLFREE(pCtx, pszFragballName); SG_VHASH_NULLFREE(pCtx, pvhRequestStatus); SG_ERR_IGNORE( SG_context__msg__emit(pCtx, "\n") ); }
static void _dump_branch_name( SG_context* pCtx, SG_console_stream cs, const char* pszRefHid, SG_bool bShowOnlyOpenBranchNames, const SG_vhash* pvhRefBranchValues, const SG_vhash* pvhRefClosedBranches) { SG_vhash* pvhRefBranchNames = NULL; if (pvhRefBranchValues) { SG_bool b_has = SG_FALSE; SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRefBranchValues, pszRefHid, &b_has) ); if (b_has) { SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhRefBranchValues, pszRefHid, &pvhRefBranchNames) ); } } if (pvhRefBranchNames) { SG_uint32 count_branch_names = 0; SG_uint32 i; SG_ERR_CHECK( SG_vhash__count(pCtx, pvhRefBranchNames, &count_branch_names) ); for (i=0; i<count_branch_names; i++) { const char* psz_branch_name = NULL; SG_bool bClosed = SG_FALSE; SG_ERR_CHECK( SG_vhash__get_nth_pair(pCtx, pvhRefBranchNames, i, &psz_branch_name, NULL) ); if (pvhRefClosedBranches) SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRefClosedBranches, psz_branch_name, &bClosed) ); if ( !bShowOnlyOpenBranchNames || (bShowOnlyOpenBranchNames && !bClosed) ) { SG_ERR_CHECK( SG_console(pCtx, cs, "\t%8s: %s%s\n", "branch", psz_branch_name, bClosed ? " (closed)" : "") ); } } } fail: ; }
static void _sg_jscontextpool__force_config_bool(SG_context * pCtx, SG_vhash * pConfig, const char * szSettingName, SG_bool * pValue) { SG_bool has = SG_FALSE; SG_bool value = SG_FALSE; SG_ASSERT(pCtx!=NULL); SG_ASSERT(pConfig!=NULL); SG_ASSERT(szSettingName!=NULL); SG_ERR_CHECK( SG_vhash__has(pCtx, pConfig, szSettingName, &has) ); if(has) { SG_uint16 type_of_value = 0; SG_ERR_CHECK( SG_vhash__typeof(pCtx, pConfig, szSettingName, &type_of_value) ); if(type_of_value==SG_VARIANT_TYPE_SZ) { const char *szValue = NULL; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pConfig, szSettingName, &szValue) ); value = strcmp(szValue, "true")==0; } else if(type_of_value==SG_VARIANT_TYPE_BOOL) { SG_ERR_CHECK( SG_vhash__get__bool(pCtx, pConfig, szSettingName, &value) ); } } SG_ERR_CHECK( SG_vhash__update__bool(pCtx, pConfig, szSettingName, value) ); if(pValue!=NULL) { *pValue = value; } return; fail: ; }
void SG_dbrecord__has_value(SG_context* pCtx, const SG_dbrecord* prec, const char* putf8Name, SG_bool* pb) { SG_bool b = SG_FALSE; SG_NULLARGCHECK_RETURN(prec); SG_NULLARGCHECK_RETURN(putf8Name); SG_NULLARGCHECK_RETURN(pb); SG_ERR_CHECK_RETURN( SG_vhash__has(pCtx, prec->pvh, putf8Name, &b) ); *pb = b; }
void SG_pull__all__list_incoming( SG_context* pCtx, const char* pszPullIntoRepoDescriptorName, SG_client* pClient, SG_varray** ppvaInfo) { sg_pull_instance_data* pMe = NULL; char* pszFragballName = NULL; SG_vhash* pvhStatus = NULL; // Used for both fragball request status returned by SG_client // and staging status returned by SG_staging. const SG_pathname* pStagingPathname; SG_NULLARGCHECK_RETURN(pszPullIntoRepoDescriptorName); SG_NULLARGCHECK_RETURN(pClient); SG_ERR_CHECK( _pull_init(pCtx, pClient, pszPullIntoRepoDescriptorName, &pMe) ); SG_ERR_CHECK( SG_staging__get_pathname(pCtx, pMe->pStaging, &pStagingPathname) ); /* Request a fragball containing leaves of every dag */ SG_ERR_CHECK_RETURN( SG_context__msg__emit(pCtx, "Retrieving dagnodes...") ); SG_ERR_CHECK( SG_client__pull_request_fragball(pCtx, pClient, NULL, pStagingPathname, &pszFragballName, &pvhStatus) ); /* Ian TODO: inspect pvhStatus */ SG_ERR_CHECK( SG_VHASH_NULLFREE(pCtx, pvhStatus) ); SG_ERR_CHECK( SG_staging__slurp_fragball(pCtx, pMe->pStaging, pszFragballName) ); SG_ERR_CHECK( SG_staging__check_status(pCtx, pMe->pStaging, SG_TRUE, SG_FALSE, SG_FALSE, SG_FALSE, SG_FALSE, &pvhStatus) ); /* Check the status and use it to request more dagnodes until the dags connect */ SG_ERR_CHECK( _add_dagnodes_until_connected(pCtx, &pvhStatus, pMe, pClient) ); SG_ERR_CHECK( SG_VHASH_NULLFREE(pCtx, pvhStatus) ); SG_ERR_CHECK( SG_staging__check_status(pCtx, pMe->pStaging, SG_FALSE, SG_FALSE, SG_TRUE, SG_FALSE, SG_FALSE, &pvhStatus) ); { SG_bool b = SG_FALSE; SG_vhash* pvhRequest = NULL; SG_ERR_CHECK( SG_vhash__has(pCtx, pvhStatus, SG_SYNC_STATUS_KEY__NEW_NODES, &b) ); if (b) { /* There are incoming nodes. Get their info. */ SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhStatus, SG_SYNC_STATUS_KEY__NEW_NODES, &pvhRequest) ); SG_ERR_CHECK( SG_client__get_dagnode_info(pCtx, pClient, pvhRequest, ppvaInfo) ); } } /* fall through */ fail: _NULLFREE_INSTANCE_DATA(pCtx, pMe); SG_VHASH_NULLFREE(pCtx, pvhStatus); SG_NULLFREE(pCtx, pszFragballName); }
void SG_vc_hooks__ASK__WIT__LIST_ITEMS( SG_context* pCtx, SG_repo* pRepo, const char * psz_search_term, SG_varray *pBugs ) { SG_vhash* pvh_params = NULL; SG_vhash* pvh_result = NULL; SG_vhash* pvh_hook = NULL; const char* psz_js = NULL; const char* psz_descriptor_name = NULL; SG_bool hasBugs = SG_FALSE; SG_ERR_CHECK( sg_vc_hooks__lookup_by_interface__single_result( pCtx, pRepo, SG_VC_HOOK__INTERFACE__ASK__WIT__LIST_ITEMS, &pvh_hook ) ); if (!pvh_hook) return; 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_repo__get_descriptor_name(pCtx, pRepo, &psz_descriptor_name) ); SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "descriptor_name", psz_descriptor_name) ); SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh_params, "text", psz_search_term) ); SG_ERR_CHECK( SG_vc_hooks__execute(pCtx, psz_js, pvh_params, &pvh_result) ); SG_ERR_CHECK( SG_vhash__has(pCtx, pvh_result, "items", &hasBugs) ); if (hasBugs && pBugs) { SG_varray *bugs = NULL; SG_ERR_CHECK( SG_vhash__get__varray(pCtx, pvh_result, "items", &bugs) ); SG_ERR_CHECK( SG_varray__copy_items(pCtx, bugs, pBugs) ); } fail: SG_VHASH_NULLFREE(pCtx, pvh_params); SG_VHASH_NULLFREE(pCtx, pvh_result); SG_VHASH_NULLFREE(pCtx, pvh_hook); }
void SG_cmd_util__dump_log( SG_context * pCtx, SG_console_stream cs, SG_repo* pRepo, const char* psz_hid_cs, SG_vhash* pvhCleanPileOfBranches, SG_bool bShowOnlyOpenBranchNames, SG_bool bShowFullComments) { SG_history_result* pHistResult = NULL; SG_stringarray * psaHids = NULL; SG_STRINGARRAY__ALLOC(pCtx, &psaHids, 1); SG_ERR_CHECK( SG_stringarray__add(pCtx, psaHids, psz_hid_cs) ); SG_history__get_revision_details(pCtx, pRepo, psaHids, NULL, &pHistResult); if (SG_context__err_equals(pCtx, SG_ERR_NOT_FOUND)) { /* There's a branch that references a changeset that doesn't exist. Show what we can. */ SG_vhash* pvhRefClosedBranches = NULL; SG_vhash* pvhRefBranchValues = NULL; SG_context__err_reset(pCtx); if (pvhCleanPileOfBranches) { SG_bool bHas = SG_FALSE; SG_ERR_CHECK( SG_vhash__has(pCtx, pvhCleanPileOfBranches, "closed", &bHas) ); if (bHas) SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhCleanPileOfBranches, "closed", &pvhRefClosedBranches) ); SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhCleanPileOfBranches, "values", &pvhRefBranchValues) ); } SG_ERR_CHECK( SG_console(pCtx, cs, "\n\t%8s: %s\n", "revision", psz_hid_cs) ); SG_ERR_CHECK( _dump_branch_name(pCtx, cs, psz_hid_cs, bShowOnlyOpenBranchNames, pvhRefBranchValues, pvhRefClosedBranches) ); SG_ERR_CHECK( SG_console(pCtx, cs, "\t%8s %s\n", "", "(not present in repository)") ); } else { SG_ERR_CHECK_CURRENT; SG_ERR_CHECK( SG_cmd_util__dump_history_results(pCtx, cs, pHistResult, pvhCleanPileOfBranches, bShowOnlyOpenBranchNames, bShowFullComments, SG_FALSE) ); } fail: SG_HISTORY_RESULT_NULLFREE(pCtx, pHistResult); SG_STRINGARRAY_NULLFREE(pCtx, psaHids); }
void SG_curl__throw_on_non200(SG_context* pCtx, SG_curl* pCurl) { SG_int32 httpResponseCode = 0; SG_vhash* pvhErr = NULL; _sg_curl* p = (_sg_curl*)pCurl; SG_NULLARGCHECK_RETURN(pCurl); SG_ERR_CHECK( SG_curl__getinfo__int32(pCtx, pCurl, CURLINFO_RESPONSE_CODE, &httpResponseCode) ); if (httpResponseCode != 200) { if ((httpResponseCode == 500 || httpResponseCode == 410) && p->pstrErr) { SG_bool bHas = SG_FALSE; const char* szMsg = NULL; SG_VHASH__ALLOC__FROM_JSON__SZ(pCtx, &pvhErr, SG_string__sz(p->pstrErr)); if (SG_context__err_equals(pCtx, SG_ERR_JSONPARSER_SYNTAX)) { // The server didn't return a JSON-formatted response. if (httpResponseCode == 500) SG_ERR_RESET_THROW2(SG_ERR_EXTENDED_HTTP_500, (pCtx, "%s", SG_string__sz(p->pstrErr))); else SG_ERR_THROW2(SG_ERR_SERVER_HTTP_ERROR, (pCtx, "%d", httpResponseCode)); } SG_ERR_CHECK_CURRENT; SG_ERR_CHECK( SG_vhash__has(pCtx, pvhErr, "msg", &bHas) ); if (bHas) SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhErr, "msg", &szMsg) ); if (szMsg) SG_ERR_THROW2(SG_ERR_EXTENDED_HTTP_500, (pCtx, "%s", szMsg)); else SG_ERR_THROW2(SG_ERR_EXTENDED_HTTP_500, (pCtx, "%s", SG_string__sz(p->pstrErr))); } else if (httpResponseCode == 401) { SG_ERR_THROW(SG_ERR_AUTHORIZATION_REQUIRED); } else SG_ERR_THROW2(SG_ERR_SERVER_HTTP_ERROR, (pCtx, "%d", httpResponseCode)); } /* common cleanup */ fail: SG_VHASH_NULLFREE(pCtx, pvhErr); }
/** * 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); }
/** * Add to the fragball request vhash (see SG_server_prototypes.h for format). */ void SG_pull__add( SG_context* pCtx, SG_pull* pPull, SG_uint32 iDagnum, SG_rbtree* prbDagnodes, SG_rbtree* prbTags, SG_rbtree* prbDagnodePrefixes) { _sg_pull* pMyPull = NULL; char bufDagnum[SG_DAGNUM__BUF_MAX__DEC]; SG_bool found = SG_FALSE; SG_vhash* pvhDags = NULL; // Needs to be freed SG_vhash* pvhDagsRef = NULL; // Does not need to be freed, owned by parent vhash SG_vhash* pvhDagnum = NULL; // Needs to be freed SG_vhash* pvhDagnumRef = NULL; // Does not need to be freed, owned by parent vhash SG_rbtree_iterator* pit = NULL; SG_NULLARGCHECK_RETURN(pPull); SG_ARGCHECK_RETURN(iDagnum, iDagnum); pMyPull = (_sg_pull*)pPull; if (!pMyPull->pvhFragballRequest) SG_ERR_CHECK( SG_VHASH__ALLOC(pCtx, &pMyPull->pvhFragballRequest) ); SG_ERR_CHECK( SG_dagnum__to_sz__decimal(pCtx, iDagnum, bufDagnum, sizeof(bufDagnum)) ); /* Get dagnum vhash, adding it if necessary. */ SG_ERR_CHECK( SG_vhash__has(pCtx, pMyPull->pvhFragballRequest, SG_SYNC_STATUS_KEY__DAGS, &found) ); if (found) SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pMyPull->pvhFragballRequest, SG_SYNC_STATUS_KEY__DAGS, &pvhDagsRef) ); else { SG_ERR_CHECK( SG_VHASH__ALLOC(pCtx, &pvhDags) ); pvhDagsRef = pvhDags; SG_ERR_CHECK( SG_vhash__add__vhash(pCtx, pMyPull->pvhFragballRequest, SG_SYNC_STATUS_KEY__DAGS, &pvhDags) ); } SG_ERR_CHECK( SG_vhash__has(pCtx, pvhDagsRef, bufDagnum, &found) ); if (found) SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhDagsRef, bufDagnum, &pvhDagnumRef) ); if (!pvhDagnumRef) { SG_ERR_CHECK( SG_VHASH__ALLOC(pCtx, &pvhDagnum) ); pvhDagnumRef = pvhDagnum; SG_ERR_CHECK( SG_vhash__add__vhash(pCtx, pvhDagsRef, bufDagnum, &pvhDagnum) ); } /* If dagnodes were provided, add them to the dagnum vhash */ if (prbDagnodes) { const char* pszHid; SG_ERR_CHECK( SG_rbtree__iterator__first(pCtx, &pit, prbDagnodes, &found, &pszHid, NULL) ); while (found) { SG_ERR_CHECK( SG_vhash__update__null(pCtx, pvhDagnumRef, pszHid) ); SG_ERR_CHECK( SG_rbtree__iterator__next(pCtx, pit, &found, &pszHid, NULL) ); } SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit); } /* If tags were provided, add them to the dagnum vhash */ if (prbTags) { const char* pszTag; SG_ERR_CHECK( SG_rbtree__iterator__first(pCtx, &pit, prbTags, &found, &pszTag, NULL) ); while (found) { SG_ERR_CHECK( SG_vhash__update__string__sz(pCtx, pvhDagnumRef, pszTag, SG_SYNC_REQUEST_VALUE_TAG) ); SG_ERR_CHECK( SG_rbtree__iterator__next(pCtx, pit, &found, &pszTag, NULL) ); } SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit); } /* If dagnode hid prefixes were provided, add them to the dagnum vhash */ if (prbDagnodePrefixes) { const char* pszHidPrefix; SG_ERR_CHECK( SG_rbtree__iterator__first(pCtx, &pit, prbDagnodePrefixes, &found, &pszHidPrefix, NULL) ); while (found) { SG_ERR_CHECK( SG_vhash__update__string__sz(pCtx, pvhDagnumRef, pszHidPrefix, SG_SYNC_REQUEST_VALUE_HID_PREFIX) ); SG_ERR_CHECK( SG_rbtree__iterator__next(pCtx, pit, &found, &pszHidPrefix, NULL) ); } SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit); } return; fail: SG_VHASH_NULLFREE(pCtx, pvhDagnum); SG_VHASH_NULLFREE(pCtx, pvhDags); SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit); }
// TODO not sure we really want to pass this much stuff to this interface void SG_vc_hooks__ASK__WIT__ADD_ASSOCIATIONS( 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_vhash* pvh_hook = NULL; SG_vhash* pvh_params = NULL; SG_vhash* pvh_result = NULL; char* psz_repo_id = NULL; char* psz_admin_id = NULL; SG_ERR_CHECK( sg_vc_hooks__lookup_by_interface__single_result( pCtx, pRepo, SG_VC_HOOK__INTERFACE__ASK__WIT__ADD_ASSOCIATIONS, &pvh_hook ) ); if (pvh_hook) { const char* psz_js = NULL; SG_uint32 i = 0; SG_varray* pva_ids = NULL; const char* psz_descriptor_name = NULL; const char* psz_csid = NULL; SG_vhash* pvh_changeset = 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_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) ); 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, &pvh_result) ); // TODO process the result if (pvh_result) { SG_bool hasErrors = SG_FALSE; SG_ERR_CHECK( SG_vhash__has(pCtx, pvh_result, "error", &hasErrors) ); if (hasErrors) { const char *emsg = NULL; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh_result, "error", &emsg) ); SG_ERR_THROW2( SG_ERR_VC_HOOK_REFUSED, (pCtx, "\n:%s: %s", SG_VC_HOOK__INTERFACE__ASK__WIT__ADD_ASSOCIATIONS, emsg) ); } } } fail: SG_VHASH_NULLFREE(pCtx, pvh_params); SG_VHASH_NULLFREE(pCtx, pvh_result); SG_VHASH_NULLFREE(pCtx, pvh_hook); SG_NULLFREE(pCtx, psz_repo_id); SG_NULLFREE(pCtx, psz_admin_id); }
/** * Do diff of an individual item. * When WC-based, we have a "DiffStep" vhash. * When historical, we have an item from a pvaStatus. * */ static void _do_diff1(SG_context * pCtx, SG_bool bWC, const SG_option_state * pOptSt, const SG_vhash * pvhItem, SG_uint32 * piResult) { SG_string * pStringGidRepoPath = NULL; SG_vhash * pvhResultCodes = NULL; SG_stringarray * psa1 = NULL; const char * pszGid; SG_int64 i64Result = 0; SG_string * pStringErr = NULL; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhItem, "gid", &pszGid) ); if (bWC) { SG_pathname * pPathWc = NULL; SG_bool bHasTool = SG_FALSE; // With the __diff__setup() and __diff__run() changes, we have already // examined the items during the __setup() step and recorded a tool for // the *FILE* that have changed content. So if "tool" isn't set in the // DiffStep/Item, we don't need to diff it -- it could be a structural // change, a non-file, a found item, etc. // // we do not use SG_wc__diff__throw() because we already have the diff info // and we want to control the result-code processing below. SG_ERR_CHECK( SG_vhash__has(pCtx, pvhItem, "tool", &bHasTool) ); if (bHasTool) SG_ERR_CHECK( SG_wc__diff__run(pCtx, pPathWc, pvhItem, &pvhResultCodes) ); } else { 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)) ); // we do not use the __throw() version of this routine so we can control // result-code processing below. SG_ERR_CHECK( SG_vv2__diff_to_stream(pCtx, pOptSt->psz_repo, pOptSt->pRevSpec, psa1, 0, SG_FALSE, // bNoSort SG_TRUE, // bInteractive, pOptSt->psz_tool, &pvhResultCodes) ); } if (pvhResultCodes) { SG_vhash * pvhResult; // we do not own this SG_ERR_CHECK( SG_vhash__check__vhash(pCtx, pvhResultCodes, pszGid, &pvhResult) ); if (pvhResult) { const char * pszTool; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhResult, "tool", &pszTool) ); SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvhResult, "result", &i64Result) ); SG_difftool__check_result_code__throw(pCtx, i64Result, pszTool); 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)) ); // eat the tool error. the result code is set. } } } if (piResult) *piResult = (SG_uint32)i64Result; fail: SG_STRING_NULLFREE(pCtx, pStringGidRepoPath); SG_VHASH_NULLFREE(pCtx, pvhResultCodes); SG_STRINGARRAY_NULLFREE(pCtx, psa1); SG_STRING_NULLFREE(pCtx, pStringErr); }
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); }
void SG_sync_remote__get_repo_info( SG_context* pCtx, SG_repo* pRepo, SG_bool bIncludeBranches, SG_bool b_include_areas, SG_vhash** ppvh) { SG_vhash* pvh = NULL; char* pszRepoId = NULL; char* pszAdminId = NULL; char* pszHashMethod = NULL; SG_uint32 count_dagnums = 0; SG_uint64* paDagNums = NULL; SG_uint32 i = 0; SG_vhash* pvh_dags = NULL; SG_vhash* pvh_areas = NULL; SG_vhash* pvhBranchPile = NULL; SG_bool bHasBranchDag = SG_FALSE; SG_NULLARGCHECK_RETURN(pRepo); SG_ERR_CHECK( SG_VHASH__ALLOC(pCtx, &pvh) ); /* Protocol version */ SG_ERR_CHECK( SG_vhash__add__int64(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__PROTOCOL_VERSION, 1) ); /* Basic repository info */ SG_ERR_CHECK( SG_repo__get_repo_id(pCtx, pRepo, &pszRepoId) ); SG_ERR_CHECK( SG_repo__get_admin_id(pCtx, pRepo, &pszAdminId) ); SG_ERR_CHECK( SG_repo__get_hash_method(pCtx, pRepo, &pszHashMethod) ); SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__REPO_ID, pszRepoId) ); SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__ADMIN_ID, pszAdminId) ); SG_ERR_CHECK( SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__HASH_METHOD, pszHashMethod) ); /* All DAGs in the repository */ SG_ERR_CHECK( SG_repo__list_dags(pCtx, pRepo, &count_dagnums, &paDagNums) ); SG_ERR_CHECK( SG_vhash__addnew__vhash(pCtx, pvh, "dags", &pvh_dags) ); for (i=0; i<count_dagnums; i++) { char buf_dagnum[SG_DAGNUM__BUF_MAX__HEX]; SG_ERR_CHECK_RETURN( SG_dagnum__to_sz__hex(pCtx, paDagNums[i], buf_dagnum, sizeof(buf_dagnum)) ); SG_ERR_CHECK( SG_vhash__add__null(pCtx, pvh_dags, buf_dagnum) ); /* Asking for a DAG for the first time in a repo will create that DAG. * When pushing into an empty repo, we don't want this initial query to create * empty new DAGs, so we make sure they exist before we query them. */ if (paDagNums[i] == SG_DAGNUM__VC_BRANCHES) bHasBranchDag = SG_TRUE; } // TODO the following code is a problem, because it requires that this repo // instance have indexes, and we would prefer to preserve the ability of // an index-free instance to support push, pull, and clone. /* All areas in the repository */ if (b_include_areas) { SG_ERR_CHECK( SG_area__list(pCtx, pRepo, &pvh_areas) ); if (pvh_areas) { SG_ERR_CHECK( SG_vhash__add__vhash(pCtx, pvh, "areas", &pvh_areas) ); } } /* Branches */ if (bIncludeBranches && bHasBranchDag) { SG_ERR_CHECK( SG_vc_branches__cleanup(pCtx, pRepo, &pvhBranchPile) ); if (pvhBranchPile) { SG_bool bHasBranches; SG_ERR_CHECK( SG_vhash__has(pCtx, pvhBranchPile, "branches", &bHasBranches) ); if (bHasBranches) SG_ERR_CHECK( SG_vhash__add__vhash(pCtx, pvh, "branches", &pvhBranchPile) ); } } *ppvh = pvh; pvh = NULL; /* fall through */ fail: SG_NULLFREE(pCtx, paDagNums); SG_VHASH_NULLFREE(pCtx, pvh); SG_NULLFREE(pCtx, pszRepoId); SG_NULLFREE(pCtx, pszAdminId); SG_NULLFREE(pCtx, pszHashMethod); SG_VHASH_NULLFREE(pCtx, pvh_areas); SG_VHASH_NULLFREE(pCtx, pvhBranchPile); }
void SG_sync_remote__request_fragball( SG_context* pCtx, SG_repo* pRepo, const SG_pathname* pFragballDirPathname, SG_vhash* pvhRequest, char** ppszFragballName) { SG_pathname* pFragballPathname = NULL; SG_uint64* paDagNums = NULL; SG_string* pstrFragballName = NULL; SG_rbtree* prbDagnodes = NULL; SG_rbtree_iterator* pit = NULL; SG_rev_spec* pRevSpec = NULL; SG_stringarray* psaFullHids = NULL; SG_rbtree* prbDagnums = NULL; SG_dagfrag* pFrag = NULL; char* pszRepoId = NULL; char* pszAdminId = NULL; SG_fragball_writer* pfb = NULL; SG_NULLARGCHECK_RETURN(pRepo); SG_NULLARGCHECK_RETURN(pFragballDirPathname); { char buf_filename[SG_TID_MAX_BUFFER_LENGTH]; SG_ERR_CHECK( SG_tid__generate(pCtx, buf_filename, sizeof(buf_filename)) ); SG_ERR_CHECK( SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pFragballPathname, pFragballDirPathname, buf_filename) ); } if (!pvhRequest) { // Add leaves from every dag to the fragball. SG_uint32 count_dagnums; SG_uint32 i; SG_ERR_CHECK( SG_fragball_writer__alloc(pCtx, pRepo, pFragballPathname, SG_TRUE, 2, &pfb) ); SG_ERR_CHECK( SG_repo__list_dags(pCtx, pRepo, &count_dagnums, &paDagNums) ); for (i=0; i<count_dagnums; i++) { SG_ERR_CHECK( SG_repo__fetch_dag_leaves(pCtx, pRepo, paDagNums[i], &prbDagnodes) ); SG_ERR_CHECK( SG_fragball__write__dagnodes(pCtx, pfb, paDagNums[i], prbDagnodes) ); SG_RBTREE_NULLFREE(pCtx, prbDagnodes); } SG_ERR_CHECK( SG_pathname__get_last(pCtx, pFragballPathname, &pstrFragballName) ); SG_ERR_CHECK( SG_STRDUP(pCtx, SG_string__sz(pstrFragballName), ppszFragballName) ); SG_ERR_CHECK( SG_fragball_writer__close(pCtx, pfb) ); } else { // Specific dags/nodes were requested. Build that fragball. SG_bool found; #if TRACE_SYNC_REMOTE && 0 SG_ERR_CHECK( SG_vhash_debug__dump_to_console__named(pCtx, pvhRequest, "fragball request") ); #endif SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__CLONE, &found) ); if (found) { // SG_SYNC_STATUS_KEY__CLONE_REQUEST is currently ignored SG_ERR_CHECK( SG_repo__fetch_repo__fragball(pCtx, pRepo, 3, pFragballDirPathname, ppszFragballName) ); } else { // Not a full clone. SG_ERR_CHECK( SG_fragball_writer__alloc(pCtx, pRepo, pFragballPathname, SG_TRUE, 2, &pfb) ); SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__SINCE, &found) ); if (found) { SG_vhash* pvh_since = NULL; SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__SINCE, &pvh_since) ); SG_ERR_CHECK( _do_since(pCtx, pRepo, pvh_since, pfb) ); } SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__DAGS, &found) ); if (found) { // Specific Dagnodes were requested. Add just those nodes to our "start from" rbtree. SG_vhash* pvhDags; SG_uint32 count_requested_dagnums; SG_uint32 i; const SG_variant* pvRevSpecs = NULL; SG_vhash* pvhRevSpec = NULL; // For each requested dag, get rev spec request. SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__DAGS, &pvhDags) ); SG_ERR_CHECK( SG_vhash__count(pCtx, pvhDags, &count_requested_dagnums) ); if (count_requested_dagnums) SG_ERR_CHECK( SG_repo__list_dags__rbtree(pCtx, pRepo, &prbDagnums) ); for (i=0; i<count_requested_dagnums; i++) { SG_bool isValidDagnum = SG_FALSE; SG_bool bSpecificNodesRequested = SG_FALSE; const char* pszRefDagNum = NULL; SG_uint64 iDagnum; // Get the dag's missing node vhash. SG_ERR_CHECK( SG_vhash__get_nth_pair(pCtx, pvhDags, i, &pszRefDagNum, &pvRevSpecs) ); // Verify that requested dagnum exists SG_ERR_CHECK( SG_rbtree__find(pCtx, prbDagnums, pszRefDagNum, &isValidDagnum, NULL) ); if (!isValidDagnum) continue; SG_ERR_CHECK( SG_dagnum__from_sz__hex(pCtx, pszRefDagNum, &iDagnum) ); if (pvRevSpecs && pvRevSpecs->type != SG_VARIANT_TYPE_NULL) { SG_uint32 countRevSpecs = 0; SG_ERR_CHECK( SG_variant__get__vhash(pCtx, pvRevSpecs, &pvhRevSpec) ); SG_ERR_CHECK( SG_rev_spec__from_vash(pCtx, pvhRevSpec, &pRevSpec) ); // Process the rev spec for each dag SG_ERR_CHECK( SG_rev_spec__count(pCtx, pRevSpec, &countRevSpecs) ); if (countRevSpecs > 0) { bSpecificNodesRequested = SG_TRUE; SG_ERR_CHECK( SG_rev_spec__get_all__repo(pCtx, pRepo, pRevSpec, SG_TRUE, &psaFullHids, NULL) ); SG_ERR_CHECK( SG_stringarray__to_rbtree_keys(pCtx, psaFullHids, &prbDagnodes) ); SG_STRINGARRAY_NULLFREE(pCtx, psaFullHids); } SG_REV_SPEC_NULLFREE(pCtx, pRevSpec); } if (!bSpecificNodesRequested) { // When no specific nodes are in the request, add all leaves. SG_ERR_CHECK( SG_repo__fetch_dag_leaves(pCtx, pRepo, iDagnum, &prbDagnodes) ); } if (prbDagnodes) // can be null when leaves of an empty dag are requested { // Get the leaves of the other repo, which we need to connect to. SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__LEAVES, &found) ); if (found) { SG_vhash* pvhRefAllLeaves; SG_vhash* pvhRefDagLeaves; SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__LEAVES, &pvhRefAllLeaves) ); SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, pszRefDagNum, &found) ); { SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhRefAllLeaves, pszRefDagNum, &pvhRefDagLeaves) ); SG_ERR_CHECK( SG_sync__build_best_guess_dagfrag(pCtx, pRepo, iDagnum, prbDagnodes, pvhRefDagLeaves, &pFrag) ); } } else { // The other repo's leaves weren't provided: add just the requested nodes, make no attempt to connect. SG_ERR_CHECK( SG_repo__get_repo_id(pCtx, pRepo, &pszRepoId) ); SG_ERR_CHECK( SG_repo__get_admin_id(pCtx, pRepo, &pszAdminId) ); SG_ERR_CHECK( SG_dagfrag__alloc(pCtx, &pFrag, pszRepoId, pszAdminId, iDagnum) ); SG_ERR_CHECK( SG_dagfrag__load_from_repo__simple(pCtx, pFrag, pRepo, prbDagnodes) ); SG_NULLFREE(pCtx, pszRepoId); SG_NULLFREE(pCtx, pszAdminId); } SG_ERR_CHECK( SG_fragball__write__frag(pCtx, pfb, pFrag) ); SG_RBTREE_NULLFREE(pCtx, prbDagnodes); SG_DAGFRAG_NULLFREE(pCtx, pFrag); } } // dagnum loop } // if "dags" exists /* Add requested blobs to the fragball */ SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__BLOBS, &found) ); if (found) { // Blobs were requested. SG_vhash* pvhBlobs; SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__BLOBS, &pvhBlobs) ); SG_ERR_CHECK( SG_sync__add_blobs_to_fragball(pCtx, pfb, pvhBlobs) ); } SG_ERR_CHECK( SG_pathname__get_last(pCtx, pFragballPathname, &pstrFragballName) ); SG_ERR_CHECK( SG_STRDUP(pCtx, SG_string__sz(pstrFragballName), ppszFragballName) ); } SG_ERR_CHECK( SG_fragball_writer__close(pCtx, pfb) ); } /* fallthru */ fail: // If we had an error, delete the half-baked fragball. if (pFragballPathname && SG_context__has_err(pCtx)) { SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pFragballPathname) ); } SG_PATHNAME_NULLFREE(pCtx, pFragballPathname); SG_NULLFREE(pCtx, paDagNums); SG_STRING_NULLFREE(pCtx, pstrFragballName); SG_RBTREE_NULLFREE(pCtx, prbDagnodes); SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit); SG_RBTREE_NULLFREE(pCtx, prbDagnums); SG_REV_SPEC_NULLFREE(pCtx, pRevSpec); SG_STRINGARRAY_NULLFREE(pCtx, psaFullHids); SG_DAGFRAG_NULLFREE(pCtx, pFrag); SG_NULLFREE(pCtx, pszRepoId); SG_NULLFREE(pCtx, pszAdminId); SG_FRAGBALL_WRITER_NULLFREE(pCtx, pfb); }
// TODO not sure we really want to pass this much stuff to this interface void SG_vc_hooks__ASK__WIT__VALIDATE_ASSOCIATIONS( SG_context* pCtx, SG_repo* pRepo, const char* const* paszAssocs, SG_uint32 count_assocs, SG_varray *pBugs ) { SG_vhash* pvh_params = NULL; SG_vhash* pvh_result = NULL; char* psz_repo_id = NULL; char* psz_admin_id = NULL; SG_vhash* pvh_hook = NULL; const char* psz_js = NULL; SG_uint32 i = 0; SG_varray* pva_ids = NULL; const char* psz_descriptor_name = NULL; SG_ERR_CHECK( sg_vc_hooks__lookup_by_interface__single_result( pCtx, pRepo, SG_VC_HOOK__INTERFACE__ASK__WIT__VALIDATE_ASSOCIATIONS, &pvh_hook ) ); if (!pvh_hook) return; 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_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, "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) ); } 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]) ); } SG_ERR_CHECK( SG_vc_hooks__execute(pCtx, psz_js, pvh_params, &pvh_result) ); // TODO process the result if (pvh_result) { SG_bool hasErrors = SG_FALSE; SG_bool hasBugs = SG_FALSE; SG_ERR_CHECK( SG_vhash__has(pCtx, pvh_result, "error", &hasErrors) ); if (hasErrors) { const char *emsg = NULL; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh_result, "error", &emsg) ); SG_ERR_THROW2( SG_ERR_VC_HOOK_REFUSED, (pCtx, "%s", emsg) ); } SG_ERR_CHECK( SG_vhash__has(pCtx, pvh_result, "bugs", &hasBugs) ); if (hasBugs && pBugs) { SG_varray *bugs = NULL; SG_ERR_CHECK( SG_vhash__get__varray(pCtx, pvh_result, "bugs", &bugs) ); SG_ERR_CHECK( SG_varray__copy_items(pCtx, bugs, pBugs) ); } } fail: SG_VHASH_NULLFREE(pCtx, pvh_params); SG_VHASH_NULLFREE(pCtx, pvh_result); SG_VHASH_NULLFREE(pCtx, pvh_hook); SG_NULLFREE(pCtx, psz_repo_id); SG_NULLFREE(pCtx, psz_admin_id); }
void SG_server__pull_request_fragball(SG_context* pCtx, SG_repo* pRepo, SG_vhash* pvhRequest, const SG_pathname* pFragballDirPathname, char** ppszFragballName, SG_vhash** ppvhStatus) { SG_pathname* pFragballPathname = NULL; SG_uint32* paDagNums = NULL; SG_rbtree* prbDagnodes = NULL; SG_string* pstrFragballName = NULL; char* pszRevFullHid = NULL; SG_rbtree_iterator* pit = NULL; SG_uint32* repoDagnums = NULL; SG_NULLARGCHECK_RETURN(pRepo); SG_NULLARGCHECK_RETURN(pFragballDirPathname); SG_NULLARGCHECK_RETURN(ppvhStatus); #if TRACE_SERVER SG_ERR_CHECK( SG_vhash_debug__dump_to_console__named(pCtx, pvhRequest, "pull fragball request") ); #endif SG_ERR_CHECK( SG_fragball__create(pCtx, pFragballDirPathname, &pFragballPathname) ); if (!pvhRequest) { // Add leaves from every dag to the fragball. SG_uint32 count_dagnums; SG_uint32 i; SG_ERR_CHECK( SG_repo__list_dags(pCtx, pRepo, &count_dagnums, &paDagNums) ); for (i=0; i<count_dagnums; i++) { SG_ERR_CHECK( SG_repo__fetch_dag_leaves(pCtx, pRepo, paDagNums[i], &prbDagnodes) ); SG_ERR_CHECK( SG_fragball__append__dagnodes(pCtx, pFragballPathname, pRepo, paDagNums[i], prbDagnodes) ); SG_RBTREE_NULLFREE(pCtx, prbDagnodes); } SG_ERR_CHECK( SG_pathname__get_last(pCtx, pFragballPathname, &pstrFragballName) ); SG_ERR_CHECK( SG_STRDUP(pCtx, SG_string__sz(pstrFragballName), ppszFragballName) ); } else { // Build the requested fragball. SG_bool found; SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__CLONE, &found) ); if (found) { // Full clone requested. SG_ERR_CHECK( SG_repo__fetch_repo__fragball(pCtx, pRepo, pFragballDirPathname, ppszFragballName) ); } else { // Not a full clone. SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__DAGS, &found) ); if (found) { // Dagnodes were requested. SG_uint32 generations = 0; SG_vhash* pvhDags; SG_uint32 count_requested_dagnums; SG_uint32 count_repo_dagnums = 0; SG_uint32 i; const char* pszDagNum = NULL; const SG_variant* pvRequestedNodes = NULL; SG_vhash* pvhRequestedNodes = NULL; const char* pszHidRequestedDagnode = NULL; // Were additional generations requested? SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__GENERATIONS, &found) ); if (found) SG_ERR_CHECK( SG_vhash__get__uint32(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__GENERATIONS, &generations) ); SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__DAGS, &pvhDags) ); SG_ERR_CHECK( SG_vhash__count(pCtx, pvhDags, &count_requested_dagnums) ); if (count_requested_dagnums) SG_ERR_CHECK( SG_repo__list_dags(pCtx, pRepo, &count_repo_dagnums, &repoDagnums) ); // For each requested dag, get the requested nodes. for (i=0; i<count_requested_dagnums; i++) { SG_uint32 iMissingNodeCount; SG_uint32 iDagnum; SG_uint32 j; SG_bool isValidDagnum = SG_FALSE; SG_bool bSpecificNodesRequested = SG_FALSE; // Get the dag's missing node vhash. SG_ERR_CHECK( SG_vhash__get_nth_pair(pCtx, pvhDags, i, &pszDagNum, &pvRequestedNodes) ); SG_ERR_CHECK( SG_dagnum__from_sz__decimal(pCtx, pszDagNum, &iDagnum) ); // Verify that requested dagnum exists for (j = 0; j < count_repo_dagnums; j++) { if (repoDagnums[j] == iDagnum) { isValidDagnum = SG_TRUE; break; } } if (!isValidDagnum) { char buf[SG_DAGNUM__BUF_MAX__NAME]; SG_ERR_CHECK( SG_dagnum__to_name(pCtx, iDagnum, buf, sizeof(buf)) ); SG_ERR_THROW2(SG_ERR_NO_SUCH_DAG, (pCtx, "%s", buf)); } if (pvRequestedNodes) { SG_ERR_CHECK( SG_variant__get__vhash(pCtx, pvRequestedNodes, &pvhRequestedNodes) ); // Get each node listed for the dag SG_ERR_CHECK( SG_vhash__count(pCtx, pvhRequestedNodes, &iMissingNodeCount) ); if (iMissingNodeCount > 0) { SG_uint32 j; const SG_variant* pvVal; bSpecificNodesRequested = SG_TRUE; SG_ERR_CHECK( SG_RBTREE__ALLOC__PARAMS(pCtx, &prbDagnodes, iMissingNodeCount, NULL) ); for (j=0; j<iMissingNodeCount; j++) { SG_ERR_CHECK( SG_vhash__get_nth_pair(pCtx, pvhRequestedNodes, j, &pszHidRequestedDagnode, &pvVal) ); if (pvVal) { const char* pszVal; SG_ERR_CHECK( SG_variant__get__sz(pCtx, pvVal, &pszVal) ); if (pszVal) { if (0 == strcmp(pszVal, SG_SYNC_REQUEST_VALUE_HID_PREFIX)) { SG_ERR_CHECK( SG_repo__hidlookup__dagnode(pCtx, pRepo, iDagnum, pszHidRequestedDagnode, &pszRevFullHid) ); pszHidRequestedDagnode = pszRevFullHid; } else if (0 == strcmp(pszVal, SG_SYNC_REQUEST_VALUE_TAG)) { SG_ERR_CHECK( SG_vc_tags__lookup__tag(pCtx, pRepo, pszHidRequestedDagnode, &pszRevFullHid) ); if (!pszRevFullHid) SG_ERR_THROW(SG_ERR_TAG_NOT_FOUND); pszHidRequestedDagnode = pszRevFullHid; } else SG_ERR_THROW(SG_ERR_PULL_INVALID_FRAGBALL_REQUEST); } } SG_ERR_CHECK( SG_rbtree__update(pCtx, prbDagnodes, pszHidRequestedDagnode) ); // Get additional dagnode generations, if requested. SG_ERR_CHECK( SG_sync__add_n_generations(pCtx, pRepo, pszHidRequestedDagnode, prbDagnodes, generations) ); SG_NULLFREE(pCtx, pszRevFullHid); } } } if (!bSpecificNodesRequested) { // When no specific nodes are in the request, add all leaves. SG_ERR_CHECK( SG_repo__fetch_dag_leaves(pCtx, pRepo, iDagnum, &prbDagnodes) ); // Get additional dagnode generations, if requested. if (generations) { SG_bool found; const char* hid; SG_ERR_CHECK( SG_rbtree__iterator__first(pCtx, &pit, prbDagnodes, &found, &hid, NULL) ); while (found) { SG_ERR_CHECK( SG_sync__add_n_generations(pCtx, pRepo, hid, prbDagnodes, generations) ); SG_ERR_CHECK( SG_rbtree__iterator__next(pCtx, pit, &found, &hid, NULL) ); } } } if (prbDagnodes) // can be null when leaves of an empty dag are requested { SG_ERR_CHECK( SG_fragball__append__dagnodes(pCtx, pFragballPathname, pRepo, iDagnum, prbDagnodes) ); SG_RBTREE_NULLFREE(pCtx, prbDagnodes); } } // dagnum loop } // if "dags" exists /* Add requested blobs to the fragball */ SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__BLOBS, &found) ); if (found) { // Blobs were requested. SG_vhash* pvhBlobs; SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__BLOBS, &pvhBlobs) ); SG_ERR_CHECK( SG_sync__add_blobs_to_fragball(pCtx, pRepo, pFragballPathname, pvhBlobs) ); } SG_ERR_CHECK( SG_pathname__get_last(pCtx, pFragballPathname, &pstrFragballName) ); SG_ERR_CHECK( SG_STRDUP(pCtx, SG_string__sz(pstrFragballName), ppszFragballName) ); } } /* fallthru */ fail: // If we had an error, delete the half-baked fragball. if (pFragballPathname && SG_context__has_err(pCtx)) SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pFragballPathname) ); SG_PATHNAME_NULLFREE(pCtx, pFragballPathname); SG_NULLFREE(pCtx, paDagNums); SG_RBTREE_NULLFREE(pCtx, prbDagnodes); SG_STRING_NULLFREE(pCtx, pstrFragballName); SG_NULLFREE(pCtx, pszRevFullHid); SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit); SG_NULLFREE(pCtx, repoDagnums); }
void SG_cmd_util__dump_history_results( SG_context * pCtx, SG_console_stream cs, SG_history_result* pHistResult, SG_vhash* pvh_pile, SG_bool bShowOnlyOpenBranchNames, SG_bool bShowFullComments, SG_bool bHideRevnums) { //print the information for each SG_bool bFound = (pHistResult != NULL); const char* currentInfoItem = NULL; SG_uint32 revno; SG_uint32 nCount = 0; SG_uint32 nIndex = 0; const char * pszTag = NULL; const char * pszComment = NULL; const char * pszStamp = NULL; const char * pszParent = NULL; SG_uint32 nResultCount = 0; SG_vhash* pvhRefBranchValues = NULL; SG_vhash* pvhRefClosedBranches = NULL; char* pszMyComment = NULL; if (pvh_pile) { SG_bool bHas = SG_FALSE; SG_ERR_CHECK( SG_vhash__has(pCtx, pvh_pile, "closed", &bHas) ); if (bHas) SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvh_pile, "closed", &pvhRefClosedBranches) ); SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvh_pile, "values", &pvhRefBranchValues) ); } SG_ERR_CHECK( SG_history_result__count(pCtx, pHistResult, &nResultCount) ); while (nResultCount != 0 && bFound) { SG_ERR_CHECK( SG_history_result__get_revno(pCtx, pHistResult, &revno) ); SG_ERR_CHECK( SG_history_result__get_cshid(pCtx, pHistResult, ¤tInfoItem) ); if(!bHideRevnums) SG_ERR_CHECK( SG_console(pCtx, cs, "\n\t%8s: %d:%s\n", "revision", revno, currentInfoItem) ); else SG_ERR_CHECK( SG_console(pCtx, cs, "\n\t%8s: %s\n", "revision", currentInfoItem) ); SG_ERR_CHECK( _dump_branch_name(pCtx, cs, currentInfoItem, bShowOnlyOpenBranchNames, pvhRefBranchValues, pvhRefClosedBranches) ); SG_ERR_CHECK( SG_history_result__get_audit__count(pCtx, pHistResult, &nCount) ); for (nIndex = 0; nIndex < nCount; nIndex++) { SG_int64 itime = -1; char buf_time_formatted[256]; const char * pszUser = NULL; SG_ERR_CHECK( SG_history_result__get_audit__who(pCtx, pHistResult, nIndex, &pszUser) ); SG_ERR_CHECK( SG_history_result__get_audit__when(pCtx, pHistResult, nIndex, &itime) ); SG_ERR_CHECK( SG_time__format_local__i64(pCtx, itime, buf_time_formatted, sizeof(buf_time_formatted)) ); SG_ERR_CHECK( SG_console(pCtx, cs, "\t%8s: %s\n", "who", pszUser) ); SG_ERR_CHECK( SG_console(pCtx, cs, "\t%8s: %s\n", "when", buf_time_formatted) ); } SG_ERR_CHECK( SG_history_result__get_tag__count(pCtx, pHistResult, &nCount) ); for (nIndex = 0; nIndex < nCount; nIndex++) { SG_ERR_CHECK( SG_history_result__get_tag__text(pCtx, pHistResult, nIndex, &pszTag) ); SG_ERR_CHECK( SG_console(pCtx, cs, "\t%8s: %s\n", "tag", pszTag) ); } SG_ERR_CHECK( SG_history_result__get_comment__count(pCtx, pHistResult, &nCount) ); for (nIndex = 0; nIndex < nCount; nIndex++) { SG_ERR_CHECK( SG_history_result__get_comment__text(pCtx, pHistResult, nIndex, &pszComment) ); if (pszComment) { SG_ERR_CHECK( _format_comment(pCtx, !bShowFullComments, "\t ", pszComment, &pszMyComment) ); if (pszMyComment) pszComment = pszMyComment; } SG_ERR_CHECK( SG_console(pCtx, cs, "\t%8s: %s\n", "comment", pszComment) ); SG_NULLFREE(pCtx, pszMyComment); } SG_ERR_CHECK( SG_history_result__get_stamp__count(pCtx, pHistResult, &nCount) ); for (nIndex = 0; nIndex < nCount; nIndex++) { SG_ERR_CHECK( SG_history_result__get_stamp__text(pCtx, pHistResult, nIndex, &pszStamp) ); SG_ERR_CHECK( SG_console(pCtx, cs, "\t%8s: %s\n", "stamp", pszStamp) ); } SG_ERR_CHECK( SG_history_result__get_parent__count(pCtx, pHistResult, &nCount) ); for (nIndex = 0; nIndex < nCount; nIndex++) { SG_ERR_CHECK( SG_history_result__get_parent(pCtx, pHistResult, nIndex, &pszParent, &revno) ); SG_ERR_CHECK( SG_console(pCtx, cs, "\t%8s: %d:%s\n", "parent", revno, pszParent) ); } SG_ERR_CHECK( SG_history_result__next(pCtx, pHistResult, &bFound) ); } fail: SG_NULLFREE(pCtx, pszMyComment); }