void SG_getopt__print_option(SG_context* pCtx, SG_console_stream cs, SG_getopt_option* opt, const char * overrideDesc) { SG_string *opts; if (opt == NULL) { SG_ERR_IGNORE( SG_console(pCtx, cs, "?") ); return; } SG_ERR_CHECK( SG_string__alloc(pCtx, &opts) ); /* We have a valid option which may or may not have a "short name" (a single-character alias for the long option). */ if (opt->optch <= 255) SG_ERR_CHECK( SG_string__sprintf(pCtx, opts, "-%c [--%s]", opt->optch, opt->pStringName) ); else SG_ERR_CHECK( SG_string__sprintf(pCtx, opts, "--%s", opt->pStringName) ); if (opt->has_arg) SG_ERR_CHECK( SG_string__append__sz(pCtx, opts, " ARG") ); if (overrideDesc) SG_ERR_IGNORE( SG_console(pCtx, cs, "%-20s : %s\n", SG_string__sz(opts), overrideDesc) ); else SG_ERR_IGNORE( SG_console(pCtx, cs, "%-20s : %s\n", SG_string__sz(opts), opt->pStringDescription) ); fail: SG_STRING_NULLFREE(pCtx, opts); }
void SG_vfile__end( SG_context* pCtx, SG_vfile** ppvf, const SG_vhash* pvh ) { SG_string* pstr = NULL; SG_vfile* pvf = NULL; SG_NULLARGCHECK_RETURN(ppvf); pvf = *ppvf; if (pvh) { SG_ARGCHECK_RETURN( !(pvf->mode & SG_FILE_RDONLY) , pvh ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr) ); SG_ERR_CHECK( SG_vhash__to_json(pCtx, pvh,pstr) ); SG_ERR_CHECK( SG_file__seek(pCtx, pvf->pFile, 0) ); SG_ERR_CHECK( SG_file__truncate(pCtx, pvf->pFile) ); #if TRACE_VFILE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "VFileEnd: Writing %d bytes to file.\n", SG_string__length_in_bytes(pstr)) ); #endif SG_ERR_CHECK( SG_file__write(pCtx, pvf->pFile, SG_string__length_in_bytes(pstr), (const SG_byte *)SG_string__sz(pstr), NULL) ); SG_STRING_NULLFREE(pCtx, pstr); } else { if (!(pvf->mode & SG_FILE_RDONLY)) { SG_ERR_CHECK( SG_file__seek(pCtx, pvf->pFile, 0) ); SG_ERR_CHECK( SG_file__truncate(pCtx, pvf->pFile) ); #if TRACE_VFILE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "VFileEnd: Truncating file.\n") ); #endif } } SG_FILE_NULLCLOSE(pCtx, pvf->pFile); SG_NULLFREE(pCtx, pvf); *ppvf = NULL; return; fail: SG_STRING_NULLFREE(pCtx, pstr); }
void _sg_mrg__copy_wc_to_temp_file(SG_context * pCtx, SG_mrg * pMrg, SG_mrg_cset_entry * pMrgCSetEntry, const SG_pathname * pPathTempFile) { sg_wc_liveview_item * pLVI; SG_string * pStringRepoPath = NULL; SG_pathname * pPathInWC = NULL; SG_bool bExists; SG_bool bKnown; SG_ERR_CHECK( SG_fsobj__exists__pathname(pCtx,pPathTempFile,&bExists,NULL,NULL) ); if (bExists) { #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx,SG_CS_STDERR, "Skipping copy of WC version to [%s]\n", SG_pathname__sz(pPathTempFile)) ); #endif return; } // Find the absolute path of the WD version of the file. SG_ERR_CHECK( sg_wc_tx__liveview__fetch_random_item(pCtx, pMrg->pWcTx, pMrgCSetEntry->uiAliasGid, &bKnown, &pLVI) ); SG_ASSERT_RELEASE_FAIL( (bKnown) ); SG_ERR_CHECK( sg_wc_tx__liveview__compute_live_repo_path(pCtx, pMrg->pWcTx, pLVI, &pStringRepoPath) ); SG_ERR_CHECK( sg_wc_db__path__repopath_to_absolute(pCtx, pMrg->pWcTx->pDb, pStringRepoPath, &pPathInWC) ); #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx,SG_CS_STDERR, "Copying WC version [%s] to [%s]\n", SG_string__sz(pStringRepoPath), SG_pathname__sz(pPathTempFile)) ); #endif // Ideally, when we create this TEMP file it should be read-only. // Afterall, it does represent a historical version of the file and // it should only be used as INPUT to whatever merge tool the user // has configured. So it should be read-only. This might allow // a GUI merge tool to show locks/whatever and/or prevent accidental // editing of these files. // // However, this can cause an "Access is Denied" error on Windows // when we get ready to delete the contents of the TEMP directory. // We'll deal with that there rather than here. SG_ERR_CHECK( SG_fsobj__copy_file(pCtx, pPathInWC, pPathTempFile, 0400) ); fail: SG_STRING_NULLFREE(pCtx, pStringRepoPath); SG_PATHNAME_NULLFREE(pCtx, pPathInWC); }
void SG_sync_remote__push_add( SG_context* pCtx, const char* pszPushId, SG_repo* pRepo, const char* psz_fragball_name, SG_vhash** ppResult ) { SG_staging* pStaging = NULL; SG_vhash* pvh_status = NULL; #if TRACE_SYNC_REMOTE SG_int64 startTime; SG_int64 endTime; double seconds; #endif SG_NULLARGCHECK_RETURN(pszPushId); SG_NULLARGCHECK_RETURN(ppResult); SG_ERR_CHECK( SG_staging__open(pCtx, pszPushId, &pStaging) ); #if TRACE_SYNC_REMOTE SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &startTime) ); #endif SG_ERR_CHECK( SG_staging__slurp_fragball(pCtx, pStaging, psz_fragball_name) ); #if TRACE_SYNC_REMOTE SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &endTime) ); seconds = ((double)endTime-(double)startTime)/1000; SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "Fragball slurp took %1.3f seconds\n", seconds) ); SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &startTime) ); #endif SG_ERR_CHECK( SG_staging__check_status(pCtx, pStaging, pRepo, SG_TRUE, SG_TRUE, SG_TRUE, SG_TRUE, SG_FALSE, &pvh_status) ); #if TRACE_SYNC_REMOTE SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &endTime) ); seconds = ((double)endTime-(double)startTime)/1000; SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "Status check after fragball slurp took %1.3f seconds\n", seconds) ); #endif *ppResult = pvh_status; pvh_status = NULL; /* fallthru */ fail: SG_VHASH_NULLFREE(pCtx, pvh_status); SG_STAGING_NULLFREE(pCtx, pStaging); }
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); }
/** * The values for RENAME, MOVE, ATTRBITS, SYMLINKS, and SUBMODULES are collapsable. (see below) * In the corresponding rbUnique's we only need to remember the set of unique values for the * field. THESE ARE THE KEYS IN THE prbUnique. * * As a convenience, we associate a vector of entries with each key. These form a many-to-one * thing so that we can report all of the entries that have this value. * * TODO since we should only process a cset once, we should not get any * TODO duplicates in the vector, but it might be possible. i'm not going * TODO to worry about it now. if this becomes a problem, consider doing * TODO a unique-insert into the vector -or- making the vector a sub-rbtree. * */ static void _update_1_rbUnique(SG_context * pCtx, SG_rbtree * prbUnique, const char * pszKey, SG_mrg_cset_entry * pMrgCSetEntry_Leaf_k) { SG_vector * pVec_Allocated = NULL; SG_vector * pVec; SG_bool bFound; SG_ERR_CHECK( SG_rbtree__find(pCtx,prbUnique,pszKey,&bFound,(void **)&pVec) ); if (!bFound) { SG_ERR_CHECK( SG_VECTOR__ALLOC(pCtx,&pVec_Allocated,3) ); SG_ERR_CHECK( SG_rbtree__add__with_assoc(pCtx,prbUnique,pszKey,pVec_Allocated) ); pVec = pVec_Allocated; pVec_Allocated = NULL; // rbtree owns this now } SG_ERR_CHECK( SG_vector__append(pCtx,pVec,pMrgCSetEntry_Leaf_k,NULL) ); #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx,SG_CS_STDERR,"_update_1_rbUnique: [%s][%s]\n", pszKey, SG_string__sz(pMrgCSetEntry_Leaf_k->pMrgCSet->pStringCSetLabel)) ); #endif return; fail: SG_VECTOR_NULLFREE(pCtx, pVec_Allocated); }
/** * Insert/Replace a TNE ROW from the tne_L0 table in the wc.db. * * The existing row (if it exists) is a copy of the TNE * as it existed in the current baseline. This item * will be present in the future baseline, but it has * one or more changed fields. So we want the TNE ROW * to be updated as we transition the tne_L0 table. * */ void sg_wc_tx__apply__insert_tne(SG_context * pCtx, SG_wc_tx * pWcTx, const SG_vhash * pvh) { #if TRACE_WC_TX_APPLY const char * pszRepoPath; SG_ERR_CHECK_RETURN( SG_vhash__get__sz(pCtx, pvh, "src", &pszRepoPath) ); SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, ("sg_wc_tx__apply__insert_tne: '%s'\n"), pszRepoPath) ); #else SG_UNUSED( pCtx ); SG_UNUSED( pvh ); #endif SG_UNUSED( pWcTx ); // we don't actually have anything here. // the journal record was more for the verbose log. // the actual work of updating the SQL will be done // in the parallel journal-stmt. }
void SG_dagfrag__foreach_member(SG_context * pCtx, SG_dagfrag * pFrag, SG_dagfrag__foreach_member_callback * pcb, void * pVoidCallerData) { // we want to iterate over the START_ and INTERIOR_ MEMBERS in the CACHE. // we need to use the SORTED MEMBER CACHE so that ancestors are presented // before descendants. struct _fm_data fm_data; SG_NULLARGCHECK_RETURN(pFrag); SG_NULLARGCHECK_RETURN(pcb); if (!pFrag->m_pRB_GenerationSortedMemberCache) SG_ERR_CHECK_RETURN( _my_create_generation_sorted_member_cache(pCtx,pFrag) ); fm_data.pFrag = pFrag; fm_data.pcb = pcb; fm_data.pVoidCallerData = pVoidCallerData; // we wrap their callback with our own so that we can munge the arguments // that they see. #if TRACE_DAGFRAG && 0 SG_ERR_CHECK_RETURN( SG_console(pCtx, SG_CS_STDERR, "SORTED MEMBER CACHE:\r\n") ); SG_ERR_CHECK_RETURN( SG_rbtree_debug__dump_keys_to_console(pCtx, pFrag->m_pRB_GenerationSortedMemberCache) ); SG_ERR_CHECK_RETURN( SG_console__flush(pCtx, SG_CS_STDERR) ); #endif SG_ERR_CHECK_RETURN( SG_rbtree__foreach(pCtx, pFrag->m_pRB_GenerationSortedMemberCache, _sg_dagfrag__my_foreach_member_callback, &fm_data) ); }
SG_error SG_context__err_to_console(SG_context* pCtx, SG_console_stream cs) { SG_error err; SG_string* pErrStr = NULL; SG_ASSERT( pCtx ); // SG_ASSERT( pCtx->level < SG_CONTEXT_MAX_ERROR_LEVELS ); if (pCtx->level > 0) // when in an error-on-error (level > 0), there is NO error string. return SG_ERR_OK; // so we don't need to do anything. // get the full error string about the error at the current level. err = SG_context__err_to_string(pCtx, &pErrStr); if (SG_IS_ERROR(err)) // an allocation/formatting error, just give up. return err; // write the error string/message to the console. we push a new level // so that any problems converting the message to the user's locale or // writing to the console device don't trash the current error context. // // ***DO NOT JUMP OUT OF THIS PUSH..POP BLOCK.*** SG_context__push_level(pCtx); { SG_console(pCtx, cs, SG_string__sz(pErrStr)); err = pCtx->errValues[pCtx->level]; // we return the error value of the conversion/writing. } SG_context__pop_level(pCtx); SG_STRING_NULLFREE(pCtx, pErrStr); return err; }
void sg_wc_tx__apply__delete_issue(SG_context * pCtx, SG_wc_tx * pWcTx, const SG_vhash * pvh) { SG_pathname * pPath = NULL; const char * pszRepoPathTempDir = NULL; // During the APPLY phase, our only task is to delete // the item's private tempdir, if it had one. Only items // with file-edit conflicts will actually have one. SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pvh, "repopath_tempdir", &pszRepoPathTempDir) ); #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "sg_wc_tx__apply__delete_issue: tempdir '%s'\n", ((pszRepoPathTempDir) ? pszRepoPathTempDir : "(null)")) ); #endif if (pszRepoPathTempDir) { SG_ERR_CHECK( sg_wc_db__path__sz_repopath_to_absolute(pCtx, pWcTx->pDb, pszRepoPathTempDir, &pPath) ); // actually deleting the tempdir can fail for any number of reasons, // but that should not abort the COMMIT. so we use _IGNORE() here // rather than _CHECK(). SG_ERR_IGNORE( SG_fsobj__rmdir_recursive__pathname(pCtx, pPath) ); } fail: SG_PATHNAME_NULLFREE(pCtx, pPath); }
void sg_wc_db__gid__prepare_toggle_tmp_stmt(SG_context * pCtx, sg_wc_db * pDb, SG_uint64 uiAliasGid, SG_bool bIsTmp, sqlite3_stmt ** ppStmt) { sqlite3_stmt * pStmt = NULL; #if TRACE_WC_DB { SG_int_to_string_buffer bufui64; SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "sg_wc_db__gid__prepare_toggle_tmp_stmt: %s --> %c\n", SG_uint64_to_sz(uiAliasGid, bufui64), ((bIsTmp) ? 'T' : 'F')) ); } #endif SG_ERR_CHECK( sg_sqlite__prepare(pCtx, pDb->psql, &pStmt, ("UPDATE tbl_gid SET tmp = ? WHERE alias_gid = ?")) ); SG_ERR_CHECK( sg_sqlite__bind_int( pCtx, pStmt, 1, bIsTmp) ); SG_ERR_CHECK( sg_sqlite__bind_int64(pCtx, pStmt, 2, uiAliasGid) ); *ppStmt = pStmt; return; fail: SG_ERR_IGNORE( sg_sqlite__finalize(pCtx, pStmt) ); }
/** * During the revert-all setup, we add certain deleted items to the * kill-list (so that we'll delete the tbl_pc row for them) effectively * marking them clean -- and we don't insert them into the pMrgCSet * directly -- we let merge discover they are missing and decide what * to do. * * But if the merge-engine discovers the item and has different plans * for it, we cancel the predicted kill for it. * */ void sg_wc_tx__merge__remove_from_kill_list(SG_context * pCtx, SG_mrg * pMrg, const SG_uint64 uiAliasGid) { SG_uint32 k, count; if (!pMrg->pVecRevertAllKillList) return; SG_ERR_CHECK( SG_vector_i64__length(pCtx, pMrg->pVecRevertAllKillList, &count) ); for (k=0; k<count; k++) { SG_uint64 uiAliasGid_k; SG_ERR_CHECK( SG_vector_i64__get(pCtx, pMrg->pVecRevertAllKillList, k, (SG_int64 *)&uiAliasGid_k) ); if (uiAliasGid_k == uiAliasGid) { #if TRACE_WC_MERGE SG_int_to_string_buffer buf; SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "Cancel kill-list for [0x%s]\n", SG_uint64_to_sz__hex(uiAliasGid_k, buf)) ); #endif SG_ERR_CHECK( SG_vector_i64__set(pCtx, pMrg->pVecRevertAllKillList, k, SG_WC_DB__ALIAS_GID__UNDEFINED) ); } } fail: return; }
/** * Prompt user for what to do on an individual item. * */ static void _do_prompt(SG_context * pCtx, const char * pszPrompt, const char * pszChoices, char chDefault, char * pchChoice) { SG_string * pStringInput = NULL; while (1) { const char * pszInput; const char * p; SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDOUT, "%s: ", pszPrompt) ); SG_ERR_CHECK( SG_console__readline_stdin(pCtx, &pStringInput) ); pszInput = SG_string__sz(pStringInput); while ((*pszInput==' ') || (*pszInput=='\t')) pszInput++; if ((*pszInput==0) || (*pszInput=='\r') || (*pszInput=='\n')) { *pchChoice = chDefault; break; } p = strchr(pszChoices, *pszInput); if (p) { *pchChoice = *p; break; } SG_STRING_NULLFREE(pCtx, pStringInput); } fail: SG_STRING_NULLFREE(pCtx, pStringInput); }
void sg_wc_db__gid__delete_all_tmp(SG_context * pCtx, sg_wc_db * pDb) { sqlite3_stmt * pStmt = NULL; #if TRACE_WC_DB SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "sg_wc_db__gid__delete_all_tmp: had %d tmp gids.\n", pDb->nrTmpGids) ); #endif if (pDb->nrTmpGids == 0) return; SG_ERR_CHECK( sg_sqlite__prepare(pCtx, pDb->psql, &pStmt, ("DELETE FROM tbl_gid WHERE tmp != 0")) ); SG_ERR_CHECK( sg_sqlite__step(pCtx, pStmt, SQLITE_DONE) ); SG_ERR_CHECK( sg_sqlite__finalize(pCtx, pStmt) ); pDb->nrTmpGids = 0; return; fail: SG_ERR_IGNORE( sg_sqlite__finalize(pCtx, pStmt) ); }
/** * remove the first item in the work-queue and return it for processing. * * You DO NOT own the returned sg_vv2status_od pointer. */ void sg_vv2__status__remove_first_from_work_queue(SG_context * pCtx, sg_vv2status * pST, SG_bool * pbFound, sg_vv2status_od ** ppOD) { const char * szKey; sg_vv2status_od * pOD; SG_bool bFound; SG_ERR_CHECK_RETURN( SG_rbtree__iterator__first(pCtx, NULL,pST->prbWorkQueue, &bFound, &szKey,(void **)&pOD) ); if (bFound) { SG_ERR_CHECK_RETURN( SG_rbtree__remove(pCtx, pST->prbWorkQueue,szKey) ); #if TRACE_VV2_STATUS SG_console(pCtx,SG_CS_STDERR,"TD_RMQUE %s (head)\n",szKey); SG_ERR_DISCARD; #endif } *pbFound = bFound; *ppOD = ((bFound) ? pOD : NULL); }
void sg_wc_db__gid__get_or_insert_alias_from_gid(SG_context * pCtx, sg_wc_db * pDb, const char * pszGid, SG_uint64 * puiAliasGid) { SG_bool bNotAlreadyPresent; sg_wc_db__gid__get_alias_from_gid(pCtx, pDb, pszGid, puiAliasGid); bNotAlreadyPresent = SG_CONTEXT__HAS_ERR(pCtx); if (bNotAlreadyPresent) { SG_context__err_reset(pCtx); SG_ERR_CHECK( sg_wc_db__gid__insert(pCtx, pDb, pszGid) ); SG_ERR_CHECK( sg_wc_db__gid__get_alias_from_gid(pCtx, pDb, pszGid, puiAliasGid) ); } #if TRACE_WC_GID { SG_int_to_string_buffer bufui64; SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GID: get_or_insert [%s] ==> %s [new %d]\n", pszGid, SG_uint64_to_sz(*puiAliasGid,bufui64), bNotAlreadyPresent) ); } #endif fail: return; }
/** * Create a per-tx temp-dir if we don't already have one. * */ void sg_wc_tx__create_session_temp_dir(SG_context * pCtx, SG_wc_tx * pWcTx) { char bufTidSession[SG_TID_MAX_BUFFER_LENGTH]; SG_uint32 nrDigits = 10; if (pWcTx->pPathSessionTempDir) return; // pick a space in /tmp for exporting temporary copies of the files // so that internal and/or external tools can compare them. // // TODO 2012/05/02 Investigate the use of SG_workingdir__get_temp_path() (which // TODO creates things in .sgdrawer rather than /tmp). // TODO (see also sg_mrg__private_file_mrg.h) // TODO See also sg_vv2__diff__create_session_temp_dir(). SG_ERR_CHECK( SG_PATHNAME__ALLOC__USER_TEMP_DIRECTORY(pCtx, &pWcTx->pPathSessionTempDir) ); SG_ERR_CHECK( SG_tid__generate2(pCtx, bufTidSession, sizeof(bufTidSession), nrDigits) ); SG_ERR_CHECK( SG_pathname__append__from_sz(pCtx, pWcTx->pPathSessionTempDir, bufTidSession) ); SG_ERR_TRY( SG_fsobj__mkdir_recursive__pathname(pCtx, pWcTx->pPathSessionTempDir) ); SG_ERR_CATCH_IGNORE( SG_ERR_DIR_ALREADY_EXISTS ); SG_ERR_CATCH_END; #if 0 && defined(DEBUG) SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "CreateSessionTempDir: %s\n", SG_pathname__sz(pWcTx->pPathSessionTempDir)) ); #endif fail: return; }
void SG_getopt__print_invalid_option(SG_context* pCtx, SG_int32 code, const SG_getopt_option* option_table) { const SG_getopt_option* pOpt; SG_getopt__get_option_from_code(code, option_table, &pOpt); if (pOpt) { if (pOpt->optch <= 255) SG_console(pCtx, SG_CS_STDERR, "-%c [--%s]\n", pOpt->optch, pOpt->pStringName); else SG_console(pCtx, SG_CS_STDERR, "--%s\n", pOpt->pStringName); } else SG_console(pCtx, SG_CS_STDERR, "-%c\n", code); }
static void _resolve__fix__run_external_file_merge_1(SG_context * pCtx, struct _resolve_data * pData, _resolve__external_tool * pET, _resolve__step_pathnames * pStepPathnames, SG_string * pStrRepoPath, SG_bool * pbMergedText) { SG_exec_argvec * pArgVec = NULL; SG_exit_status exitStatus; SG_UNUSED( pData ); #if 0 && defined(DEBUG) SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, ("RESOLVE File Merge: %s\n" " Mine: %s\n" " Other: %s\n" " Ancestor: %s\n" " Result: %s\n"), SG_string__sz(pStrRepoPath), SG_pathname__sz(pStepPathnames->pPath_Mine), SG_pathname__sz(pStepPathnames->pPath_Other), SG_pathname__sz(pStepPathnames->pPath_Ancestor), SG_pathname__sz(pStepPathnames->pPath_Result)) ); #endif SG_ERR_CHECK( SG_exec_argvec__alloc(pCtx, &pArgVec) ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, "-r") ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, SG_pathname__sz(pStepPathnames->pPath_Result)) ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, "-t1") ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, "Mine") ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, "-t2") ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, SG_string__sz(pStrRepoPath)) ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, "-t3") ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, "Other") ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, SG_pathname__sz(pStepPathnames->pPath_Mine)) ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, SG_pathname__sz(pStepPathnames->pPath_Ancestor)) ); SG_ERR_CHECK( SG_exec_argvec__append__sz(pCtx, pArgVec, SG_pathname__sz(pStepPathnames->pPath_Other)) ); SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDOUT, "RESOLVE: Preparing to launch external merge tool: %s\n", pET->pszName) ); SG_ERR_CHECK( SG_exec__exec_sync__files(pCtx, pET->pszExe, pArgVec, NULL, NULL, NULL, &exitStatus) ); *pbMergedText = (exitStatus == 0); fail: SG_EXEC_ARGVEC_NULLFREE(pCtx, pArgVec); }
/** * Export the contents of the given file entry (which reflects a specific version) * into a temp file so that an external tool can use it. * * You own the returned pathname and the file on disk. */ void _sg_mrg__export_to_temp_file(SG_context * pCtx, SG_mrg * pMrg, const char * pszHidBlob, const SG_pathname * pPathTempFile) { SG_file * pFile = NULL; SG_bool bExists; SG_ERR_CHECK( SG_fsobj__exists__pathname(pCtx,pPathTempFile,&bExists,NULL,NULL) ); if (bExists) { #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx,SG_CS_STDERR, "Skipping export of [blob %s] to [%s]\n",pszHidBlob,SG_pathname__sz(pPathTempFile)) ); #endif } else { #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx,SG_CS_STDERR, "Exporting [blob %s] to [%s]\n",pszHidBlob,SG_pathname__sz(pPathTempFile)) ); #endif // Ideally, when we create this TEMP file it should be read-only. // Afterall, it does represent a historical version of the file and // it should only be used as INPUT to whatever merge tool the user // has configured. So it should be read-only. This might allow // a GUI merge tool to show locks/whatever and/or prevent accidental // editing of these files. // // However, this can cause an "Access is Denied" error on Windows // when we get ready to delete the contents of the TEMP directory. // We'll deal with that there rather than here. SG_ERR_CHECK( SG_file__open__pathname(pCtx,pPathTempFile,SG_FILE_WRONLY|SG_FILE_CREATE_NEW,0400,&pFile) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_file(pCtx, pMrg->pWcTx->pDb->pRepo, pszHidBlob,pFile,NULL) ); SG_FILE_NULLCLOSE(pCtx,pFile); } return; fail: if (pFile) // only if **WE** created the file, do we try to delete it on error. { SG_FILE_NULLCLOSE(pCtx,pFile); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx,pPathTempFile) ); } }
/** * Dump the commitFlags for the entire tree. * */ void sg_wc_tx__commit__dump_marks(SG_context * pCtx, SG_wc_tx * pWcTx, const char * pszLabel) { SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "CommitDump: %s\n", pszLabel) ); SG_ERR_CHECK_RETURN( _dump_marks__lvi(pCtx, pWcTx, pWcTx->pLiveViewItem_Root, 0, " ", SG_TRUE) ); }
void sg_wc_db__debug__tne_row__print(SG_context * pCtx, sg_wc_db__tne_row * pTneRow) { SG_int_to_string_buffer bufui64_a; SG_int_to_string_buffer bufui64_b; SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "sg_wc_db:tne_row: [gid %s][par %s][hid %s] %s\n", SG_uint64_to_sz(pTneRow->p_s->uiAliasGid, bufui64_a), SG_uint64_to_sz(pTneRow->p_s->uiAliasGidParent, bufui64_b), pTneRow->p_d->pszHid, pTneRow->p_s->pszEntryname) ); }
void sg_wc_tx__apply__move_rename(SG_context * pCtx, SG_wc_tx * pWcTx, const SG_vhash * pvh) { SG_bool bAfterTheFact; SG_bool bUseIntermediate; SG_bool bSrcIsSparse; const char * pszRepoPath_Src; const char * pszRepoPath_Dest; SG_pathname * pPath_Src = NULL; SG_pathname * pPath_Dest = NULL; SG_pathname * pPath_Temp = NULL; SG_ERR_CHECK( SG_vhash__get__bool(pCtx, pvh, "after_the_fact", &bAfterTheFact) ); SG_ERR_CHECK( SG_vhash__get__bool(pCtx, pvh, "use_intermediate", &bUseIntermediate) ); SG_ERR_CHECK( SG_vhash__get__bool(pCtx, pvh, "src_sparse", &bSrcIsSparse) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh, "src", &pszRepoPath_Src) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh, "dest", &pszRepoPath_Dest) ); #if TRACE_WC_TX_APPLY SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, ("sg_wc_tx__apply__move_rename: [after-the-fact %d][use-intermediate %d][src-sparse %d] '%s' --> '%s'\n"), bAfterTheFact, bUseIntermediate, bSrcIsSparse, pszRepoPath_Src, pszRepoPath_Dest) ); #endif if (bAfterTheFact || bSrcIsSparse) // user already did the move/rename. return; SG_ERR_CHECK( sg_wc_db__path__sz_repopath_to_absolute(pCtx, pWcTx->pDb, pszRepoPath_Src, &pPath_Src ) ); SG_ERR_CHECK( sg_wc_db__path__sz_repopath_to_absolute(pCtx, pWcTx->pDb, pszRepoPath_Dest, &pPath_Dest) ); if (bUseIntermediate) { // need to use a temp file because of transient // collisions ('vv rename foo FOO' on Windows). SG_ERR_CHECK( sg_wc_db__path__get_unique_temp_path(pCtx, pWcTx->pDb, &pPath_Temp) ); SG_ERR_CHECK( SG_fsobj__move__pathname_pathname(pCtx, pPath_Src, pPath_Temp) ); SG_ERR_CHECK( SG_fsobj__move__pathname_pathname(pCtx, pPath_Temp, pPath_Dest) ); } else { SG_ERR_CHECK( SG_fsobj__move__pathname_pathname(pCtx, pPath_Src, pPath_Dest) ); } fail: SG_PATHNAME_NULLFREE(pCtx, pPath_Src); SG_PATHNAME_NULLFREE(pCtx, pPath_Dest); SG_PATHNAME_NULLFREE(pCtx, pPath_Temp); }
void sg_wc_tx__merge__determine_target_cset(SG_context * pCtx, SG_mrg * pMrg) { SG_ERR_CHECK( _merge__compute_target_hid(pCtx, pMrg) ); #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "Merge: target (%s).\n", pMrg->pszHidTarget) ); #endif fail: return; }
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: ; }
void sg_wc_tx__fake_merge__revert_all__determine_target_cset(SG_context * pCtx, SG_mrg * pMrg) { SG_ERR_CHECK( SG_strdup(pCtx, pMrg->pszHid_StartingBaseline, &pMrg->pszHidTarget) ); // Don't worry about pMrg->pvhPile; it is just a cache and loaded as needed. #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "FakeMergeRevertAll: target (%s).\n", pMrg->pszHidTarget) ); #endif fail: return; }
void SG_dagfrag_debug__dump__console(SG_context* pCtx, SG_dagfrag* pFrag, const char* szLabel, SG_uint32 indent, SG_console_stream cs) { SG_string* pString; SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pString) ); SG_ERR_CHECK( SG_dagfrag_debug__dump(pCtx, pFrag, szLabel, indent, pString) ); SG_ERR_CHECK( SG_console(pCtx, cs, SG_string__sz(pString)) ); SG_ERR_CHECK( SG_console__flush(pCtx, cs) ); // fall through fail: SG_STRING_NULLFREE(pCtx, pString); }
/** * The values for RENAME, MOVE, ATTRBITS, SYMLINKS, and SUBMODULES are collapsable. (see below) * In the corresponding rbUnique's we only need to remember the set of unique values for the * field. THESE ARE THE KEYS IN THE prbUnique. * * As a convenience, we associate a vector of entries with each key. These form a many-to-one * thing so that we can report all of the entries that have this value. * * Here we carry-forward the values from a sub-merge to the outer-merge by coping the keys * in the source-rbtree and insert in the destination-rbtree. * * NOTE: the term sub-merge here refers to the steps within an n-way merge; * it DOES NOT refer to a submodule. */ static void _carry_forward_unique_values(SG_context * pCtx, SG_rbtree * prbDest, SG_rbtree * prbSrc) { SG_rbtree_iterator * pIter = NULL; SG_vector * pVec_Allocated = NULL; const char * pszKey; SG_vector * pVec_Src; SG_vector * pVec_Dest; SG_uint32 j, nr; SG_bool bFound; SG_ERR_CHECK( SG_rbtree__iterator__first(pCtx,&pIter,prbSrc,&bFound,&pszKey,(void **)&pVec_Src) ); while (bFound) { SG_ERR_CHECK( SG_rbtree__find(pCtx,prbDest,pszKey,&bFound,(void **)&pVec_Dest) ); if (!bFound) { SG_ERR_CHECK( SG_VECTOR__ALLOC(pCtx,&pVec_Allocated,3) ); SG_ERR_CHECK( SG_rbtree__add__with_assoc(pCtx,prbDest,pszKey,pVec_Allocated) ); pVec_Dest = pVec_Allocated; pVec_Allocated = NULL; // rbtree owns this now } SG_ERR_CHECK( SG_vector__length(pCtx,pVec_Src,&nr) ); for (j=0; j<nr; j++) { SG_mrg_cset_entry * pMrgCSetEntry_x; SG_ERR_CHECK( SG_vector__get(pCtx,pVec_Src,j,(void **)&pMrgCSetEntry_x) ); SG_ERR_CHECK( SG_vector__append(pCtx,pVec_Dest,pMrgCSetEntry_x,NULL) ); #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx,SG_CS_STDERR,"_carry_forward_unique_value: [%s][%s]\n", pszKey, SG_string__sz(pMrgCSetEntry_x->pMrgCSet->pStringCSetLabel)) ); #endif } SG_ERR_CHECK( SG_rbtree__iterator__next(pCtx,pIter,&bFound,&pszKey,NULL) ); } fail: SG_RBTREE_ITERATOR_NULLFREE(pCtx,pIter); }
/** * This routine is used by UPDATE to fake a MERGE; that is, to do * a one-legged merge. This routine must do set the same fields * in pMrg that the above routine does. * * I've isolated the effects here because UPDATE has already done * most of the homework/validation and we don't need to step thru * the stuff again here. * */ void sg_wc_tx__fake_merge__update__determine_target_cset(SG_context * pCtx, SG_mrg * pMrg, const char * pszHidTarget) { SG_ERR_CHECK( SG_strdup(pCtx, pszHidTarget, &pMrg->pszHidTarget) ); // Don't worry about pMrg->pvhPile; it is just a cache and loaded as needed. #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "FakeMergeUpdate: target (%s).\n", pMrg->pszHidTarget) ); #endif fail: return; }
static void _dump_marks__lvi(SG_context * pCtx, SG_wc_tx * pWcTx, sg_wc_liveview_item * pLVI, SG_uint32 indent, const char * pszPrefix, SG_bool bDive) { SG_int_to_string_buffer bufui64; SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "%*c %02x 0x%s %s %s\n", indent, ' ', pLVI->commitFlags, SG_uint64_to_sz__hex((SG_uint64)pLVI->statusFlags_commit,bufui64), pszPrefix, SG_string__sz(pLVI->pStringEntryname)) ); if ((bDive) && (pLVI->tneType == SG_TREENODEENTRY_TYPE_DIRECTORY)) { sg_wc_liveview_dir * pLVD; // we do not own this struct dump_data dump_data; dump_data.pWcTx = pWcTx; dump_data.indent = indent + 4; SG_ERR_CHECK( sg_wc_tx__liveview__fetch_dir(pCtx, pWcTx, pLVI, &pLVD) ); if (pLVD->prb64LiveViewItems) { SG_ERR_CHECK( sg_wc_liveview_dir__foreach(pCtx, pLVD, _dump_marks__dir__current__cb, &dump_data) ); } if (pLVD->prb64MovedOutItems) { SG_ERR_CHECK( sg_wc_liveview_dir__foreach_moved_out(pCtx, pLVD, _dump_marks__dir__moved_out__cb, &dump_data) ); } } fail: return; }