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:
	;
}
Beispiel #3
0
/**
 * 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);
}