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); }
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: ; }
/** * Try to FIX the ISSUE. * * Alter something in the pendingtree/issue/WD and then SAVE the pendingtree. * We allow this to be an incremental save after just this issue. We also * allow the VFILE lock to be released while the external merge (DiffMerge) * tool is running. (Not because DiffMerge needs it, but rather so that they * could do other STATUS/DIFF commands in another shell while doing the text * merge.) */ static void _resolve__fix(SG_context * pCtx, struct _resolve_data * pData, const char * pszGid, enum _fix_status * pFixStatus) { const SG_vhash * pvhIssue; SG_string * pStrRepoPath = NULL; SG_int64 i64; SG_mrg_cset_entry_conflict_flags conflict_flags; SG_portability_flags portability_flags; SG_bool bIsResolved = SG_FALSE; SG_bool bCollisions; // Fetch the ISSUE using the current pendingtree (allocating one if // necessary) and print detailed info about the ISSUE on the console. SG_ERR_CHECK( _resolve__lookup_issue(pCtx, pData, pszGid, &pvhIssue) ); SG_ERR_CHECK( _resolve__list(pCtx, pData, pvhIssue, &pStrRepoPath) ); // TODO 2010/07/12 We should have a --prompt option to allow them to // TODO skip an issue. Like "/bin/rm -i *". // Skip the issue if it is already resolved. In theory, we should not // get this (because we filtered the pData->psaGids by status as we // parsed the command line arguments), but if they did another resolve // in another shell while we didn't have the lock, it could happen. SG_ERR_CHECK( _resolve__is_resolved(pCtx, pvhIssue, &bIsResolved) ); if (bIsResolved) { SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDOUT, "Issue already resolved; nothing to be done for '%s'.\n", SG_string__sz(pStrRepoPath)) ); goto done; } // There are 2 main types of problems: // [1] Conflicts within the text of a file (where the builtin auto-merge // failed or was not used) and for which we need to ask them to manually // merge the content (using an external tool like DiffMerge). // [2] Structural changes, including: MOVEs, RENAMEs, CHMODs, XATTRs, // entryname collisions, potential entryname collisions, and etc. // // We could also have both -- both edit conflicts and rename conflicts, // for example. // // Do these in 2 steps so that we can release the VFILE lock while they // are editing the file. ////////////////////////////////////////////////////////////////// // [1] ////////////////////////////////////////////////////////////////// SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvhIssue, "conflict_flags", &i64) ); conflict_flags = (SG_mrg_cset_entry_conflict_flags)i64; if (conflict_flags & SG_MRG_CSET_ENTRY_CONFLICT_FLAGS__DIVERGENT_FILE_EDIT__MASK__NOT_OK) { SG_ERR_CHECK( _resolve__fix__run_external_file_merge(pCtx, pData, pszGid, pvhIssue, pStrRepoPath, pFixStatus) ); pvhIssue = NULL; if (*pFixStatus != FIX_USER_MERGED) goto done; // the above MAY have freed and reloaded the pendingtree (and // invalidated pvhIssue), so re-fetch it and/or re-set our variables. 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. *pFixStatus = FIX_LOST_RACE; goto done; } SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvhIssue, "conflict_flags", &i64) ); conflict_flags = (SG_mrg_cset_entry_conflict_flags)i64; } #if 0 && defined(DEBUG) SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "RESOLVE: Issue between [1] and [2]: '%s'\n", SG_string__sz(pStrRepoPath)) ); SG_ERR_IGNORE( SG_vhash_debug__dump_to_console(pCtx, pvhIssue) ); #endif ////////////////////////////////////////////////////////////////// // [2] ////////////////////////////////////////////////////////////////// SG_ERR_CHECK( SG_vhash__get__bool(pCtx, pvhIssue, "collision_flags", &bCollisions) ); SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvhIssue, "portability_flags", &i64) ); portability_flags = (SG_portability_flags)i64; if (conflict_flags & SG_MRG_CSET_ENTRY_CONFLICT_FLAGS__UNDELETE__MASK) { SG_ERR_CHECK( _resolve__fix__structural__delete(pCtx, pData, pszGid, pvhIssue, pStrRepoPath, conflict_flags, bCollisions, portability_flags, pFixStatus) ); if (*pFixStatus != FIX_USER_MERGED) goto done; } else if (bCollisions || (portability_flags) || (conflict_flags & ~SG_MRG_CSET_ENTRY_CONFLICT_FLAGS__DIVERGENT_FILE_EDIT__MASK)) { SG_ERR_CHECK( _resolve__fix__structural__non_delete(pCtx, pData, pszGid, pvhIssue, pStrRepoPath, conflict_flags, bCollisions, portability_flags, pFixStatus) ); if (*pFixStatus != FIX_USER_MERGED) goto done; } // mark the issue as RESOLVED and save the pendingtree. SG_ERR_CHECK( _resolve__mark(pCtx, pData, pvhIssue, SG_TRUE) ); *pFixStatus = FIX_USER_MERGED; ////////////////////////////////////////////////////////////////// // We've completely resolved the issue, if there were ~mine files, // we can delete them. if (conflict_flags & SG_MRG_CSET_ENTRY_CONFLICT_FLAGS__DIVERGENT_FILE_EDIT__MASK__NOT_OK) { SG_ERR_CHECK( _resolve__lookup_issue(pCtx, pData, pszGid, &pvhIssue) ); SG_ERR_CHECK( _resolve__delete_temp_files(pCtx, pData, pszGid, pvhIssue) ); } done: ; fail: SG_STRING_NULLFREE(pCtx, pStrRepoPath); }
void sg_wc_tx__apply__store_symlink(SG_context * pCtx, SG_wc_tx * pWcTx, const SG_vhash * pvh) { SG_pathname * pPath = NULL; SG_string * pStringSymlink = NULL; const char * pszRepoPath; // we do not own this const char * pszHidExpected; // we do not own this char * pszHidObserved = NULL; sg_wc_liveview_item * pLVI; // we do not own this SG_int64 alias; SG_bool bKnown; SG_bool bDontBother_BlobEncoding; SG_bool bSrcIsSparse; SG_ERR_CHECK( SG_vhash__get__sz( pCtx, pvh, "src", &pszRepoPath) ); SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvh, "alias", &alias) ); SG_ERR_CHECK( SG_vhash__get__sz( pCtx, pvh, "hid", &pszHidExpected) ); SG_ERR_CHECK( SG_vhash__get__bool( pCtx, pvh, "src_sparse", &bSrcIsSparse) ); #if TRACE_WC_TX_APPLY SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, ("sg_wc_tx__apply__store_symlink: '%s' [src-sparse %d]\n"), pszRepoPath, bSrcIsSparse) ); #endif SG_ERR_CHECK( sg_wc_tx__liveview__fetch_random_item(pCtx, pWcTx, alias, &bKnown, &pLVI) ); SG_ASSERT( (bSrcIsSparse == SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_SPARSE(pLVI->scan_flags_Live)) ); if (bSrcIsSparse) { // We've been asked to store the target of the symlink ***during a COMMIT*** // and are given the *Expected-HID* (and we need to get the actual target // from the WD) and it is assumed that that will generate the same HID // that we were given. // // However, if the symlink is sparse (not populated) we can't do __readlink() // to get the (current) target. So we have to // assume that we already have a blob in the repo for it. // // Since sparse items now have p_d_sparse dynamic data in tbl_PC, we assume // that whoever last modified the content of the symlink and set p_d_sparse->pszHid // also recorded the blob we need to be present now. (See __apply__overwrite_symlink()) // // for sanity's sake verify that we already have this blob in the repo. SG_uint64 len = 0; SG_ERR_CHECK( SG_repo__fetch_blob__begin(pCtx, pWcTx->pDb->pRepo, pszHidExpected, SG_TRUE, NULL, NULL, NULL, &len, NULL) ); // so we don't need to do anything because we already // have a copy of this blob in the repo. return; } // We never bother compressing/encoding the symlink content // since it is so short. bDontBother_BlobEncoding = SG_TRUE; SG_ERR_CHECK( sg_wc_db__path__sz_repopath_to_absolute(pCtx, pWcTx->pDb, pszRepoPath, &pPath) ); SG_ERR_CHECK( SG_fsobj__readlink(pCtx, pPath, &pStringSymlink) ); SG_ERR_CHECK( SG_committing__add_bytes__string(pCtx, pWcTx->pCommittingInProgress, pStringSymlink, bDontBother_BlobEncoding, &pszHidObserved) ); // See note in __apply__store_file() about race condition. // If the HID computed now differs from what we thought // it should be, we lost the race. if (strcmp(pszHidObserved, pszHidExpected) != 0) SG_ERR_THROW2( SG_ERR_ASSERT, (pCtx, "The symlink '%s' changed during the commit.", pszRepoPath) ); fail: SG_PATHNAME_NULLFREE(pCtx, pPath); SG_STRING_NULLFREE(pCtx, pStringSymlink); SG_NULLFREE(pCtx, pszHidObserved); }