Beispiel #1
0
static void _deserialize_data_ver_1_cb(SG_context * pCtx,
									   void * pVoidDeserializeData,
									   SG_UNUSED_PARAM(const SG_varray * pva),
									   SG_UNUSED_PARAM(SG_uint32 ndx),
									   const SG_variant * pVariant)
{
	struct _deserialize_data * pDeserializeData = (struct _deserialize_data *)pVoidDeserializeData;
	SG_vhash * pvhMyData;
	SG_vhash * pvhDagnode;
	SG_int64 gen64, state64;
	_my_data * pMyData;
	SG_dagnode * pDagnode = NULL;
    const char* psz_id = NULL;

	SG_UNUSED(pva);
	SG_UNUSED(ndx);


	SG_ERR_CHECK(  SG_variant__get__vhash(pCtx,pVariant,&pvhMyData)  );
#if DEBUG && TRACE_DAGFRAG && 0
	SG_ERR_CHECK(  SG_vhash_debug__dump_to_console(pCtx, pvhMyData)  );
#endif

	SG_ERR_CHECK(  SG_vhash__get__int64(pCtx,pvhMyData,KEY_DFS_STATE,&state64)  );

    if (SG_DFS_END_FRINGE == state64)
    {
        SG_ERR_CHECK(  SG_vhash__get__sz(pCtx,pvhMyData,KEY_DAGNODE_ID,&psz_id)  );
		SG_ERR_CHECK(  _cache__add__fringe(pCtx,pDeserializeData->pFrag, psz_id)  );
    }
    else
    {
        SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx,pvhMyData,KEY_ACTUAL_DAGNODE,&pvhDagnode)  );
		SG_ERR_CHECK(  SG_vhash__get__int64(pCtx,pvhDagnode,KEY_GEN,&gen64)  );
        SG_ERR_CHECK(  SG_dagnode__alloc__from_vhash(pCtx, &pDagnode, pvhDagnode)  );

        SG_ERR_CHECK(  _cache__add__dagnode(pCtx,
											pDeserializeData->pFrag,
											(SG_int32)gen64,
											pDagnode,
											(SG_uint32)state64,
											&pMyData)  );
        pDagnode = NULL;	// cache owns it now.
    }
	return;

fail:
	SG_DAGNODE_NULLFREE(pCtx, pDagnode);
}
Beispiel #2
0
/**
 * We need to check/recheck the RESOLVED/UNRESOLVED status each time
 * after we release the VFILE lock (because the user could have done
 * another RESOLVE in another shell window while the external merge
 * tool was running).
 */
static void _resolve__is_resolved(SG_context * pCtx,
								  const SG_vhash * pvhIssue,
								  SG_bool * pbIsResolved)
{
	SG_int64 s64;
	SG_pendingtree_wd_issue_status status;

	SG_ERR_CHECK_RETURN(  SG_vhash__get__int64(pCtx, pvhIssue, "status", &s64)  );
	status = (SG_pendingtree_wd_issue_status)s64;

	*pbIsResolved = ((status & SG_ISSUE_STATUS__MARKED_RESOLVED) == SG_ISSUE_STATUS__MARKED_RESOLVED);
}
Beispiel #3
0
/**
 * build an ordered stringarray of the GIDs of all of the issues.
 * i need to have the list of GIDs be independent of pPendingTree
 * and pvaIssues so that FIX can do incremental SAVES of the
 * pendingtree (which trashes the ptnodes and/or requires a reload).
 */
static void _resolve__get_all_issue_gids(SG_context * pCtx,
										 struct _resolve_data * pData,
										 SG_bool bWantResolved,
										 SG_bool bWantUnresolved)
{
	const SG_varray * pvaIssues;			// varray[pvhIssue *] of all issues -- we do not own this
	SG_bool bHaveIssues;
	SG_uint32 k;
	SG_uint32 nrIssues = 0;
	SG_bool bWantBoth = (bWantResolved && bWantUnresolved);

	SG_ERR_CHECK(  SG_pendingtree__get_wd_issues__ref(pCtx, pData->pPendingTree, &bHaveIssues, &pvaIssues)  );
	if (bHaveIssues)
		SG_ERR_CHECK(  SG_varray__count(pCtx, pvaIssues, &nrIssues)  );

	SG_ERR_CHECK(  SG_STRINGARRAY__ALLOC(pCtx, &pData->psaGids, nrIssues)  );

	for (k=0; k<nrIssues; k++)
	{
		const SG_vhash * pvhIssue_k;
		const char * pszGid_k;
		SG_bool bWantThisOne;

		SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pvaIssues, k, (SG_vhash **)&pvhIssue_k)  );

		if (bWantBoth)
			bWantThisOne = SG_TRUE;
		else
		{
			SG_int64 s;
			SG_pendingtree_wd_issue_status status;
			SG_bool bResolved;

			SG_ERR_CHECK_RETURN(  SG_vhash__get__int64(pCtx, pvhIssue_k, "status", &s)  );
			status = (SG_pendingtree_wd_issue_status)s;
			bResolved = ((status & SG_ISSUE_STATUS__MARKED_RESOLVED) == SG_ISSUE_STATUS__MARKED_RESOLVED);

			bWantThisOne = ((bWantResolved && bResolved) || (bWantUnresolved && !bResolved));
		}

		if (bWantThisOne)
		{
			SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhIssue_k, "gid", &pszGid_k)  );
			SG_ERR_CHECK(  SG_stringarray__add(pCtx, pData->psaGids, pszGid_k)  );
		}
	}

fail:
	return;
}
Beispiel #4
0
void SG_dagfrag__alloc__from_vhash(SG_context * pCtx,
								   SG_dagfrag ** ppNew,
								   const SG_vhash * pvhFrag)
{
	const char * szVersion;
	SG_dagfrag * pFrag = NULL;
	struct _deserialize_data deserialize_data;
    SG_int64 iDagNum = 0;

	SG_NULLARGCHECK_RETURN(ppNew);
	SG_NULLARGCHECK_RETURN(pvhFrag);

	SG_ERR_CHECK(  SG_vhash__get__sz(pCtx,pvhFrag,KEY_VERSION,&szVersion)  );
	if (strcmp(szVersion,"1") == 0)
	{
		// handle dagfrags that were serialized by software compiled with
		// VALUE_VERSION == 1.

		SG_varray * pvaMyData;
        const char* psz_repo_id = NULL;
        const char* psz_admin_id = NULL;

        SG_ERR_CHECK(  SG_vhash__get__sz(pCtx,pvhFrag,KEY_REPO_ID,&psz_repo_id)  );
        SG_ERR_CHECK(  SG_vhash__get__sz(pCtx,pvhFrag,KEY_ADMIN_ID,&psz_admin_id)  );
        SG_ERR_CHECK(  SG_vhash__get__int64(pCtx,pvhFrag,KEY_DAGNUM,&iDagNum)  );

        SG_ERR_CHECK(  SG_dagfrag__alloc(pCtx,&pFrag,psz_repo_id,psz_admin_id,(SG_uint32) iDagNum)  );

		SG_ERR_CHECK(  SG_vhash__get__varray(pCtx,pvhFrag,KEY_DATA,&pvaMyData)  );

		deserialize_data.pFrag = pFrag;
		SG_ERR_CHECK(  SG_varray__foreach(pCtx,
										  pvaMyData,
										  _deserialize_data_ver_1_cb,
										  &deserialize_data)  );

		*ppNew = pFrag;
		return;
	}
	else
	{
		SG_ERR_THROW(  SG_ERR_DAGFRAG_DESERIALIZATION_VERSION  );
	}

fail:
	SG_DAGFRAG_NULLFREE(pCtx, pFrag);
}
/**
 * Pick thru the computed VARRAY build a subset VARRAY
 * containing just the dirty files.  That is, for an interactive
 * diff, we only show dirty files.  (In batch/patch mode, we show
 * everything.)
 *
 * We assume that the varray looks like:
 *
 * varray := [ { "status" : { "flags" : <int>,
 *                            ... },
 *               "path"   : <repo-path>,
 *               ... },
 *             ... ];
 *
 * Both a Canonical STATUS (pvaStatus) and a "DiffStep" (pvaDiffStep)
 * match this pattern.
 *
 * Return NULL if there aren't any.
 *
 */
static void _get_dirty_files(SG_context * pCtx,
                             const SG_varray * pva,
                             SG_varray ** ppvaDirtyFiles)
{
    SG_varray * pvaDirtyFiles = NULL;
    SG_uint32 k, nrItems;

    *ppvaDirtyFiles = NULL;

    if (!pva)
        return;
    SG_ERR_CHECK(  SG_varray__count(pCtx, pva, &nrItems)  );
    if (nrItems == 0)
        return;

    for (k=0; k<nrItems; k++)
    {
        SG_vhash * pvhItem_k;			// we do not own this
        SG_vhash * pvhItemStatus_k;		// we do not own this
        SG_int64 i64;
        SG_wc_status_flags statusFlags;

        SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva, k, &pvhItem_k)  );
        SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhItem_k, "status", &pvhItemStatus_k)  );
        SG_ERR_CHECK(  SG_vhash__get__int64(pCtx, pvhItemStatus_k, "flags", &i64)  );
        statusFlags = (SG_wc_status_flags)i64;

        if ((statusFlags & SG_WC_STATUS_FLAGS__T__FILE) == 0)
            continue;
        if ((statusFlags & (SG_WC_STATUS_FLAGS__C__NON_DIR_MODIFIED
                            |SG_WC_STATUS_FLAGS__S__ADDED
                            |SG_WC_STATUS_FLAGS__S__DELETED
                            |SG_WC_STATUS_FLAGS__S__MERGE_CREATED
                            |SG_WC_STATUS_FLAGS__S__UPDATE_CREATED)) == 0)
            continue;

        if (!pvaDirtyFiles)
            SG_ERR_CHECK(  SG_VARRAY__ALLOC(pCtx, &pvaDirtyFiles)  );
        SG_ERR_CHECK(  SG_varray__appendcopy__vhash(pCtx, pvaDirtyFiles, pvhItem_k, NULL)  );
    }

    SG_RETURN_AND_NULL( pvaDirtyFiles, ppvaDirtyFiles );

fail:
    SG_VARRAY_NULLFREE(pCtx, pvaDirtyFiles);
}
Beispiel #6
0
/**
 * Update the RESOLVED/UNRESOLVED status for this ISSUE and do an
 * incremental save on the pendingtree.
 */
static void _resolve__mark(SG_context * pCtx,
						   struct _resolve_data * pData,
						   const SG_vhash * pvhIssue,
						   SG_bool bMarkResolved)
{
	SG_int64 s;
	SG_pendingtree_wd_issue_status status;

	SG_ERR_CHECK_RETURN(  SG_vhash__get__int64(pCtx, pvhIssue, "status", &s)  );
	status = (SG_pendingtree_wd_issue_status)s;

	if (bMarkResolved)
		status |= SG_ISSUE_STATUS__MARKED_RESOLVED;
	else
		status &= ~SG_ISSUE_STATUS__MARKED_RESOLVED;

	// update the status on the ISSUE and save the pendingtree now.
	// since this trashes stuff within in it, go ahead and free it
	// so no one trips over the trash.

	SG_ERR_CHECK_RETURN(  SG_pendingtree__set_wd_issue_status(pCtx, pData->pPendingTree, pvhIssue, status)  );
	SG_PENDINGTREE_NULLFREE(pCtx, pData->pPendingTree);
}
Beispiel #7
0
static void _resolve__do_mark_1(SG_context * pCtx,
								struct _resolve_data * pData,
								const char * pszGid,
								SG_bool bMarkResolved)
{
	const SG_vhash * pvhIssue;
	SG_bool bNeedToDeleteTempFiles = SG_FALSE;

	SG_ERR_CHECK(  _resolve__lookup_issue(pCtx, pData, pszGid, &pvhIssue)  );

	if (bMarkResolved)
	{
		SG_int64 i64;
		SG_mrg_cset_entry_conflict_flags conflict_flags;

		// see if there could possibly be ~mine files for this issue that we should delete.

		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)
			bNeedToDeleteTempFiles = SG_TRUE;
	}

	SG_ERR_CHECK(  _resolve__mark(pCtx, pData, pvhIssue, bMarkResolved)  );

	// After the pendingtree has been written out, try to delete the temp files.

	if (bNeedToDeleteTempFiles)
	{
		SG_ERR_CHECK(  _resolve__lookup_issue(pCtx, pData, pszGid, &pvhIssue)  );
		SG_ERR_CHECK(  _resolve__delete_temp_files(pCtx, pData, pszGid, pvhIssue)  );
	}

fail:
	return;
}
/**
 * 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);
}
Beispiel #9
0
/**
 * Assuming that we have something of the form:
 * 
 *     vv resolve [--foo] <arg_0> [<arg_1> [<arg_2> ...]]
 *
 * where each <arg_x> is an absolute or relative path in the WD
 * (probably not a repo-path).
 *
 * Use the PENDINGTREE to lookup each path and get the entry's GID.
 * Use the GID to search for an ISSUE in the list of issues.  If we
 * find it, add the GID to the stringarray we are building.  If not,
 * throw an error.
 */
static void _resolve__map_args_to_gids(SG_context * pCtx,
									   struct _resolve_data * pData,
									   SG_uint32 count_args, const char ** paszArgs,
									   SG_bool bWantResolved,
									   SG_bool bWantUnresolved)
{
	SG_pathname * pPath_k = NULL;
	char * pszGid_k = NULL;
	SG_uint32 kArg;
	SG_bool bWantBoth = (bWantResolved && bWantUnresolved);

	SG_ERR_CHECK(  SG_STRINGARRAY__ALLOC(pCtx, &pData->psaGids, count_args)  );

	for (kArg=0; kArg<count_args; kArg++)
	{
		const SG_vhash * pvhIssue_k;
		SG_bool bFound;
		SG_bool bDuplicate;
		SG_bool bWantThisOne;

		// take each <arg_k> and get a full pathname for it and
		// search for it in the pendingtree and get its GID.
		// in theory, if an entry has an issue, it is dirty and
		// should have a ptnode.

		if (paszArgs[kArg][0] == '@')
			SG_ERR_CHECK(  SG_workingdir__construct_absolute_path_from_repo_path2(pCtx, pData->pPendingTree, paszArgs[kArg], &pPath_k)  );
		else
			SG_ERR_CHECK(  SG_PATHNAME__ALLOC__SZ(pCtx, &pPath_k, paszArgs[kArg])  );

		SG_ERR_CHECK(  SG_pendingtree__get_gid_from_local_path(pCtx, pData->pPendingTree, pPath_k, &pszGid_k)  );
#if 0 && defined(DEBUG)
		SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR,
								   ("Mapped arg[%d] '%s' to:\n"
									"\t%s\n"
									"\t[gid %s]\n"),
								   kArg, paszArgs[kArg],
								   SG_pathname__sz(pPath_k),
								   pszGid_k)  );
#endif

		// see if there is an ISSUE for this GID.

		SG_ERR_CHECK(  SG_pendingtree__find_wd_issue_by_gid(pCtx, pData->pPendingTree, pszGid_k, &bFound, &pvhIssue_k)  );
		if (!bFound)
			SG_ERR_THROW2(  SG_ERR_ISSUE_NOT_FOUND,
							(pCtx, "No issue found for '%s': %s",
							 paszArgs[kArg],
							 SG_pathname__sz(pPath_k))  );

		if (bWantBoth)
			bWantThisOne = SG_TRUE;
		else
		{
			SG_int64 s;
			SG_pendingtree_wd_issue_status status;
			SG_bool bResolved;

			SG_ERR_CHECK_RETURN(  SG_vhash__get__int64(pCtx, pvhIssue_k, "status", &s)  );
			status = (SG_pendingtree_wd_issue_status)s;
			bResolved = ((status & SG_ISSUE_STATUS__MARKED_RESOLVED) == SG_ISSUE_STATUS__MARKED_RESOLVED);

			bWantThisOne = ((bWantResolved && bResolved) || (bWantUnresolved && !bResolved));
		}

		if (bWantThisOne)
		{
			// check for duplicate args on command line. (or rather, args that
			// map to the same GID.)

			SG_ERR_CHECK(  SG_stringarray__find(pCtx, pData->psaGids, pszGid_k, 0, &bDuplicate, NULL)  );
			if (bDuplicate)
				SG_ERR_THROW2(  SG_ERR_DUPLICATE_ISSUE,
								(pCtx, "Argument '%s' maps to an issue already named.", paszArgs[kArg])  );

			SG_ERR_CHECK(  SG_stringarray__add(pCtx, pData->psaGids, pszGid_k)  );
		}

		SG_NULLFREE(pCtx, pszGid_k);
		SG_PATHNAME_NULLFREE(pCtx, pPath_k);
	}

	return;

fail:
	SG_NULLFREE(pCtx, pszGid_k);
	SG_PATHNAME_NULLFREE(pCtx, pPath_k);
}
Beispiel #10
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);
}
Beispiel #11
0
/**
 * Release VFILE lock and invoke external merge tool for this file.
 *
 * TODO 2010/07/12 The MERGE-PLAN is an array and allows for
 * TODO            multiple steps (for an n-way sub-merge cascade).
 * TODO            But we don't have that part turned on yet in
 * TODO            sg_mrg__private_biuld_wd_issues.h:_make_file_merge_plan(),
 * TODO            so for now, we only expect 1 step.
 * TODO
 * TODO            Also, when we do have multiple steps, we might want to
 * TODO            be able to use the 'status' field to see which steps
 * TODO            were already performed in an earlier RESOLVE.
 * TODO
 * TODO            Also, when we want to support more than 1 step we need
 * TODO            to copy pvaPlan because when we release the pendingtree
 * TODO            the pvhIssue becomes invalidated too.
 */
static void _resolve__fix__run_external_file_merge(SG_context * pCtx,
												   struct _resolve_data * pData,
												   const char * pszGid,
												   const SG_vhash * pvhIssue,
												   SG_string * pStrRepoPath,
												   enum _fix_status * pFixStatus)
{
	_resolve__step_pathnames * pStepPathnames = NULL;
	_resolve__external_tool * pET = NULL;
	const SG_varray * pvaPlan;
	const SG_vhash * pvhStep_0;
	SG_int64 r64;
	SG_uint32 nrSteps;
	SG_mrg_automerge_result result;
	SG_bool bMerged = SG_FALSE;
	SG_bool bIsResolved = SG_FALSE;

	SG_ERR_CHECK(  SG_vhash__get__varray(pCtx, pvhIssue, "conflict_file_merge_plan", (SG_varray **)&pvaPlan)  );
	SG_ERR_CHECK(  SG_varray__count(pCtx, pvaPlan, &nrSteps)  );

	if (nrSteps > 1)
		SG_ERR_THROW2(  SG_ERR_ASSERT,
						(pCtx, "TODO RESOLVE more than 1 step in auto-merge plan for '%s'.", SG_string__sz(pStrRepoPath))  );

	//////////////////////////////////////////////////////////////////
	// Get Step[0]

	SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pvaPlan, 0, (SG_vhash **)&pvhStep_0)  );

	// see if the user has already performed the merge and maybe got interrupted.

	SG_ERR_CHECK(  SG_vhash__get__int64(pCtx, pvhStep_0, "status", &r64)  );
	result = (SG_mrg_automerge_result)r64;
	if (result == SG_MRG_AUTOMERGE_RESULT__SUCCESSFUL)
	{
		SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR,
								   "TODO Print message about previous successful manual merge of the file content and ask if they want to redo it for '%s'.\n",
								   SG_string__sz(pStrRepoPath))  );

		*pFixStatus = FIX_USER_MERGED;
		goto done;
	}

	SG_ERR_CHECK(  _resolve__step_pathnames__compute(pCtx, pData, pvhIssue, pvhStep_0, pStrRepoPath, &pStepPathnames)  );

	// While we still have a handle to the pendingtree, lookup the
	// specifics on the external tool that we should invoke.  these
	// details come from localsettings.

	SG_ERR_CHECK(  _resolve__external_tool__lookup(pCtx, pData, pszGid, pvhIssue, pStrRepoPath, &pET)  );

	// Free the PENDINGTREE so that we release the VFILE lock.

	pvhIssue = NULL;
	pvaPlan = NULL;
	pvhStep_0 = NULL;
	SG_PENDINGTREE_NULLFREE(pCtx, pData->pPendingTree);

	//////////////////////////////////////////////////////////////////
	// Invoke the external tool.

	SG_ERR_CHECK(  _resolve__fix__run_external_file_merge_1(pCtx,
															pData,
															pET,
															pStepPathnames,
															pStrRepoPath,
															&bMerged)  );
	if (!bMerged)
	{
		SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDOUT, "RESOLVE: Aborting the merge of this file.\n")  );
		*pFixStatus = FIX_USER_ABORTED;
		goto done;
	}

	//////////////////////////////////////////////////////////////////
	// Reload the PENDINGTREE and re-fetch the ISSUE and updated the STATUS on
	// this step in the PLAN.
	// 
	// We duplicate some of the "see if someone else resolved this issue while
	// we were without the lock" stuff.

	SG_ERR_CHECK(  _resolve__lookup_issue(pCtx, pData, pszGid, &pvhIssue)  );
	SG_ERR_CHECK(  _resolve__is_resolved(pCtx, pvhIssue, &bIsResolved)  );
	if (bIsResolved)
	{
		// Someone else marked it resolved while were waiting for
		// the user to edit the file and while we didn't have the
		// file lock.  We should stop here.

		SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDOUT, "RESOLVE: Aborting the merge of this file (due to race condition).\n")  );
		*pFixStatus = FIX_LOST_RACE;
		goto done;
	}

	// re-fetch the current step and update the "result" status for it
	// and flush the pendingtree back disk.
	//
	// we only update the step status -- we DO NOT alter the __DIVERGENT_FILE_EDIT__
	// conflict_flags.

	SG_ERR_CHECK(  SG_vhash__get__varray(pCtx, pvhIssue, "conflict_file_merge_plan", (SG_varray **)&pvaPlan)  );
	SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pvaPlan, 0, (SG_vhash **)&pvhStep_0)  );
	SG_ERR_CHECK(  SG_pendingtree__set_wd_issue_plan_step_status__dont_save_pendingtree(pCtx, pData->pPendingTree,
																						pvhStep_0,
																						SG_MRG_AUTOMERGE_RESULT__SUCCESSFUL)  );
	SG_ERR_CHECK(  SG_pendingtree__save(pCtx, pData->pPendingTree)  );
	SG_PENDINGTREE_NULLFREE(pCtx, pData->pPendingTree);

	SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDOUT, "RESOLVE: The file content portion of the merge was successful.\n")  );
	*pFixStatus = FIX_USER_MERGED;

	// we defer the delete of the temp input files until we completely
	// resolve the issue.  (This gives us more options if we allow the
	// resolve to be restarted after interruptions.)

done:
	;
fail:
	_RESOLVE__EXTERNAL_TOOL__NULLFREE(pCtx, pET);
	_RESOLVE__STEP_PATHNAMES__NULLFREE(pCtx, pStepPathnames);
}
/**
 * Get the current attrbits observed on the item *UNIONED*
 * with the baseline, if present.
 *
 * If the item is LOST/DELETED (not present in the working
 * directory), fall back to the baseline value.
 *
 */
void sg_wc_liveview_item__get_current_attrbits(SG_context * pCtx,
											   sg_wc_liveview_item * pLVI,
											   SG_wc_tx * pWcTx,
											   SG_uint64 * pAttrbits)
{
	if (pLVI->queuedOverwrites.pvhAttrbits)
	{
		// We have a QUEUED operation on this item that set the attrbits.
		// get the value from the journal.

		SG_ERR_CHECK_RETURN(  SG_vhash__get__int64(pCtx, pLVI->queuedOverwrites.pvhAttrbits,
												   "attrbits",
												   (SG_int64 *)pAttrbits)  );
#if TRACE_WC_LIE
		{
			SG_int_to_string_buffer bufui64;
			SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR,
									   "GetCurrentAttrbits: using journal: %s\n",
									   SG_uint64_to_sz__hex((*pAttrbits), bufui64))  );
		}
#endif
		return;
	}

#if 1 && TRACE_WC_ATTRBITS
	{
	sg_wc_db__pc_row__flags_net flags_net = SG_WC_DB__PC_ROW__FLAGS_NET__ZERO;
	SG_ERR_IGNORE(  sg_wc_liveview_item__get_flags_net(pCtx, pLVI, &flags_net)  );
	SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR,
							   "GetCurrentAttrbits: [flagsLive %d][flagsNet 0x%02x][pPcRow_PC %04x][pPcRow_Ref %04x][pRD %04x][pTneRow %04x] %s\n",
							   ((SG_uint32)pLVI->scan_flags_Live),
							   ((SG_uint32)flags_net),
							   (SG_uint32)((pLVI->pPcRow_PC) ? (pLVI->pPcRow_PC->ref_attrbits) : 0xffff),
							   (SG_uint32)((pLVI->pPrescanRow->pPcRow_Ref) ? (pLVI->pPrescanRow->pPcRow_Ref->ref_attrbits) : 0xffff),
							   (SG_uint32)((pLVI->pPrescanRow->pRD && pLVI->pPrescanRow->pRD->pAttrbits) ? (*pLVI->pPrescanRow->pRD->pAttrbits) : 0xffff),
							   (SG_uint32)((pLVI->pPrescanRow->pTneRow && pLVI->pPrescanRow->pTneRow->p_d) ? (pLVI->pPrescanRow->pTneRow->p_d->attrbits) : 0xffff),
							   SG_string__sz(pLVI->pStringEntryname))  );
	}
#endif

	SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow) );

	if (SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_SPARSE(pLVI->scan_flags_Live))
	{
		if (pLVI->pPcRow_PC)
		{
			SG_ASSERT_RELEASE_RETURN( (pLVI->pPcRow_PC->p_d_sparse) );
			*pAttrbits = pLVI->pPcRow_PC->p_d_sparse->attrbits;
		}
		else if (pLVI->pPrescanRow->pPcRow_Ref)
		{
			SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse) );
			*pAttrbits = pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse->attrbits;
		}
		else
		{
			SG_ERR_THROW2_RETURN(  SG_ERR_NOTIMPLEMENTED,
								   (pCtx, "GetCurrentAttrbits: unhandled case when sparse for '%s'.",
									SG_string__sz(pLVI->pStringEntryname))  );
		}
	}
	else if (pLVI->pPrescanRow->pRD)
	{
		SG_ERR_CHECK_RETURN(  sg_wc_readdir__row__get_attrbits(pCtx, pWcTx,
															   pLVI->pPrescanRow->pRD)  );
		if (pLVI->pPcRow_PC)
			SG_ERR_CHECK_RETURN(  sg_wc_attrbits__compute_effective_attrbits(pCtx,
																			 pWcTx->pDb->pAttrbitsData,
																			 pLVI->pPcRow_PC->ref_attrbits,
																			 *pLVI->pPrescanRow->pRD->pAttrbits,
																			 pAttrbits)  );
		else if (pLVI->pPrescanRow->pPcRow_Ref)
			SG_ERR_CHECK_RETURN(  sg_wc_attrbits__compute_effective_attrbits(pCtx,
																			 pWcTx->pDb->pAttrbitsData,
																			 pLVI->pPrescanRow->pPcRow_Ref->ref_attrbits,
																			 *pLVI->pPrescanRow->pRD->pAttrbits,
																			 pAttrbits)  );
		else if (pLVI->pPrescanRow->pTneRow)
			SG_ERR_CHECK_RETURN(  sg_wc_attrbits__compute_effective_attrbits(pCtx,
																			 pWcTx->pDb->pAttrbitsData,
																			 pLVI->pPrescanRow->pTneRow->p_d->attrbits,
																			 *pLVI->pPrescanRow->pRD->pAttrbits,
																			 pAttrbits)  );
		else
			*pAttrbits = *pLVI->pPrescanRow->pRD->pAttrbits;
	}
	else if (pLVI->pPrescanRow->pTneRow)
	{
		*pAttrbits = pLVI->pPrescanRow->pTneRow->p_d->attrbits;
	}
	else
	{
		*pAttrbits = SG_WC_ATTRBITS__ZERO;
	}

#if 1 && TRACE_WC_ATTRBITS
	SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR,
							   "GetCurrentAttrbits: yielded %x: %s\n",
							   (SG_uint32)(*pAttrbits),
							   SG_string__sz(pLVI->pStringEntryname))  );
#endif
}
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);
}