void sg_wc__status__find_status__single_item(SG_context * pCtx,
									  const SG_varray * pva_statuses,
									  const char * psz_repo_path, //This must be the repo path, exactly as it appears in the status results.
									  SG_vhash ** ppvh_status_result)
{
	SG_vhash * pvh_status = NULL;
	SG_uint32 nArraySize = 0;
	SG_uint32 index = 0;
	const char * psz_repo_path_to_check = NULL;
	SG_bool bFound = SG_FALSE;
	SG_NULLARGCHECK(pva_statuses);

	SG_ERR_CHECK(  SG_varray__count(pCtx, pva_statuses, &nArraySize)  );

	for (index = 0; index < nArraySize; index++)
	{
		SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva_statuses, index, &pvh_status)  );
		SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvh_status, "path", &psz_repo_path_to_check)  );
		if (SG_strcmp__null(psz_repo_path, psz_repo_path_to_check) == 0)
		{
			bFound = SG_TRUE;
			break;
		}
	}
	if (bFound)
		SG_RETURN_AND_NULL(pvh_status, ppvh_status_result);
fail:
	return;
}
Ejemplo n.º 2
0
/**
 * Given an issue that has a file-content conflict and has just been resolved,
 * go thru all of the steps in the merge plan and delete the ~mine files.
 */
static void _resolve__delete_temp_files(SG_context * pCtx,
										struct _resolve_data * pData,
										const char * pszGid,
										const SG_vhash * pvhIssue)
{
	_resolve__step_pathnames * pStepPathnames = NULL;
	const SG_varray * pvaPlan;
	SG_uint32 kStep, nrSteps;

	SG_UNUSED( pszGid );

	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)  );

	for (kStep=0; kStep<nrSteps; kStep++)
	{
		const SG_vhash * pvhStep;

		SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pvaPlan, kStep, (SG_vhash **)&pvhStep)  );

		SG_ERR_CHECK(  _resolve__step_pathnames__compute(pCtx, pData, pvhIssue, pvhStep, NULL, &pStepPathnames)  );
		SG_ERR_CHECK(  _resolve__step_pathnames__delete_temp_files(pCtx, pStepPathnames)  );
		_RESOLVE__STEP_PATHNAMES__NULLFREE(pCtx, pStepPathnames);
	}

fail:
	_RESOLVE__STEP_PATHNAMES__NULLFREE(pCtx, pStepPathnames);
}
static void _user_match_found(SG_context * pCtx, SG_repo * pRepo, _node_t *pNode1, _node_t * pNode2, SG_bool *matchFound)
{
	SG_uint32 countAudits1 = 0;
	SG_uint32 countAudits2 = 0;
	SG_uint32 iAudit1;

	if(pNode1->pAudits==NULL)
	{
		SG_ERR_CHECK(  SG_repo__lookup_audits(pCtx, pRepo, SG_DAGNUM__VERSION_CONTROL, pNode1->pszHidRef, &pNode1->pAudits)  );
	}
	if(pNode2->pAudits==NULL)
	{
		SG_ERR_CHECK(  SG_repo__lookup_audits(pCtx, pRepo, SG_DAGNUM__VERSION_CONTROL, pNode2->pszHidRef, &pNode2->pAudits)  );
	}


	SG_ERR_CHECK(  SG_varray__count(pCtx, pNode1->pAudits, &countAudits1)  );
	SG_ERR_CHECK(  SG_varray__count(pCtx, pNode2->pAudits, &countAudits2)  );
	for(iAudit1=0; iAudit1<countAudits1; ++iAudit1)
	{
		SG_vhash * pAuditRef = NULL;
		SG_uint32 iAudit2;

		const char * szAudit1 = NULL;
		SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pNode1->pAudits, iAudit1, &pAuditRef)  );
		SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pAuditRef, "userid", &szAudit1)  );

		for(iAudit2=0; iAudit2<countAudits2; ++iAudit2)
		{
			const char * szAudit2 = NULL;
			SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pNode2->pAudits, iAudit2, &pAuditRef)  );
			SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pAuditRef, "userid", &szAudit2)  );
			if(strcmp(szAudit1, szAudit2)==0)
			{
				*matchFound = SG_TRUE;
				return;
			}
		}
	}

	*matchFound = SG_FALSE;

	return;
fail:
	;
}
Ejemplo n.º 4
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;
}
/**
 * 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);
}
/**
 * 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);
}
/**
 * Compute DIFF on (baseline or arbitrary cset vs WC) and either
 * splat to console or launch a GUI tool for each.
 *
 */
static void _s01__do_cset_vs_wc(SG_context * pCtx,
                                const SG_option_state * pOptSt,
                                const SG_stringarray * psaArgs,
                                SG_uint32 * pNrErrors)
{
    SG_wc_tx * pWcTx = NULL;
    SG_varray * pvaDiffSteps = NULL;
    SG_varray * pvaDiffSteps_DirtyFiles = NULL;
    SG_pathname * pPathWc = NULL;
    SG_uint32 nrErrors = 0;

    SG_ERR_CHECK(  SG_WC_TX__ALLOC__BEGIN(pCtx, &pWcTx, pPathWc, SG_TRUE)  );
    SG_ERR_CHECK(  SG_wc_tx__diff__setup__stringarray(pCtx, pWcTx, pOptSt->pRevSpec,
                   psaArgs,
                   WC__GET_DEPTH(pOptSt),
                   SG_FALSE, // bNoIgnores
                   SG_FALSE,	// bNoTSC
                   SG_FALSE,	// bNoSort,
                   pOptSt->bInteractive,
                   pOptSt->psz_tool,
                   &pvaDiffSteps)  );
    // rollback/cancel the TX to release the SQL locks,
    // but don't free it yet (because that will auto-delete
    // the session temp files that we are using for the left
    // sides).
    //
    // This is like SG_wc__diff__throw() but we want to control
    // the diff-loop so we can optionally do the interactive prompt.

    SG_ERR_CHECK(  SG_wc_tx__cancel(pCtx, pWcTx)  );

    if (pvaDiffSteps)
    {
        if (pOptSt->bInteractive)
        {
            SG_ERR_CHECK(  _get_dirty_files(pCtx, pvaDiffSteps, &pvaDiffSteps_DirtyFiles)  );
            if (pvaDiffSteps_DirtyFiles)
                SG_ERR_CHECK(  _do_gui_diffs(pCtx, SG_TRUE, pOptSt, pvaDiffSteps_DirtyFiles, &nrErrors)  );
        }
        else
        {
            SG_uint32 k, nrItems;

            SG_ERR_CHECK(  SG_varray__count(pCtx, pvaDiffSteps, &nrItems)  );
            for (k=0; k<nrItems; k++)
            {
                SG_vhash * pvhItem;
                const char * pszHeader = NULL;
                SG_uint32 iResult;

                SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pvaDiffSteps, k, &pvhItem)  );
                SG_ERR_CHECK(  SG_vhash__check__sz(pCtx, pvhItem, "header", &pszHeader)  );
                if (pszHeader)
                    SG_ERR_IGNORE(  SG_console__raw(pCtx, SG_CS_STDOUT, pszHeader)  );
                SG_ERR_CHECK(  _do_diff1(pCtx, SG_TRUE, pOptSt, pvhItem, &iResult)  );

                switch (iResult)
                {
                default:
                case SG_FILETOOL__RESULT__SUCCESS:
                case SG_DIFFTOOL__RESULT__SAME:
                case SG_DIFFTOOL__RESULT__DIFFERENT:
                    break;

                case SG_DIFFTOOL__RESULT__CANCEL:
                case SG_FILETOOL__RESULT__FAILURE:
                case SG_FILETOOL__RESULT__ERROR:
                    nrErrors++;
                    break;
                }
            }
        }
    }

    *pNrErrors = nrErrors;

fail:
    SG_VARRAY_NULLFREE(pCtx, pvaDiffSteps);
    SG_VARRAY_NULLFREE(pCtx, pvaDiffSteps_DirtyFiles);
    SG_WC_TX__NULLFREE(pCtx, pWcTx);
}
static void _s2__do_cset_vs_cset(SG_context * pCtx,
                                 const SG_option_state * pOptSt,
                                 const SG_stringarray * psaArgs,
                                 SG_uint32 * pNrErrors)
{
    SG_varray * pvaStatus = NULL;
    SG_varray * pvaStatusDirtyFiles = NULL;
    SG_stringarray * psa1 = NULL;
    SG_string * pStringGidRepoPath = NULL;
    SG_string * pStringErr = NULL;
    SG_uint32 nrErrors = 0;

    SG_ERR_CHECK(  SG_vv2__status(pCtx,
                                  pOptSt->psz_repo,
                                  pOptSt->pRevSpec,
                                  psaArgs,
                                  WC__GET_DEPTH(pOptSt),
                                  SG_FALSE, // bNoSort
                                  &pvaStatus, NULL)  );
    if (pvaStatus)
    {
        if (pOptSt->bInteractive)
        {
            // Filter list down to just modified files and show them one-by-one.
            SG_ERR_CHECK(  _get_dirty_files(pCtx, pvaStatus, &pvaStatusDirtyFiles)  );
            if (pvaStatusDirtyFiles)
                SG_ERR_CHECK(  _do_gui_diffs(pCtx, SG_FALSE, pOptSt, pvaStatusDirtyFiles, &nrErrors)  );
        }
        else
        {
            SG_uint32 k, nrItems;

            // Print the changes with PATCH-like headers.
            // Accumulate any tool errors.
            SG_ERR_CHECK(  SG_varray__count(pCtx, pvaStatus, &nrItems)  );
            for (k=0; k<nrItems; k++)
            {
                SG_vhash * pvhItem;
                const char * pszGid = NULL;

                SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pvaStatus, k, &pvhItem)  );

                // TODO 2013/02/22 Our pvhItem has all of the details for the diff,
                // TODO            but we don't yet have a public API to let it be
                // TODO            used as is.  So we build a @gid repo-path and
                // TODO            run the old historical diff code on a 1-item array
                // TODO            containing this @gid.
                // TODO
                // TODO            We should fix this to just pass down the pvhItem
                // TOOD            so that it doesn't have to repeat the status lookup.

                SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhItem, "gid", &pszGid)  );
                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))  );

                SG_vv2__diff_to_stream__throw(pCtx, pOptSt->psz_repo, pOptSt->pRevSpec,
                                              psa1, 0,
                                              SG_TRUE,		// bNoSort -- doesn't matter only 1 item in list
                                              SG_FALSE,		// bInteractive,
                                              pOptSt->psz_tool);
                // Don't throw the error from the tool.  Just print it on STDERR
                // and remember that we had an error so that don't stop showing
                // the diffs just because we stumble over a changed binary file
                // or mis-configured tool, for example.
                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))  );
                    SG_STRING_NULLFREE(pCtx, pStringErr);

                    nrErrors++;
                }

                SG_STRING_NULLFREE(pCtx, pStringGidRepoPath);
                SG_STRINGARRAY_NULLFREE(pCtx, psa1);
            }
        }
    }

    *pNrErrors = nrErrors;

fail:
    SG_VARRAY_NULLFREE(pCtx, pvaStatus);
    SG_VARRAY_NULLFREE(pCtx, pvaStatusDirtyFiles);
    SG_STRINGARRAY_NULLFREE(pCtx, psa1);
    SG_STRING_NULLFREE(pCtx, pStringGidRepoPath);
    SG_STRING_NULLFREE(pCtx, pStringErr);
}
/**
 * Iterate over all of the dirty files and prompt before diffing.
 * pvaDirtyFiles can either be a STATUS or a "DiffStep" varray.
 *
 */
static void _do_gui_diffs(SG_context * pCtx,
                          SG_bool bWC,
                          const SG_option_state * pOptSt,
                          const SG_varray * pvaDirtyFiles,
                          SG_uint32 * pNrErrors)
{
    SG_uint32 k, nrItems;
    SG_uint32 nrErrors = 0;

    SG_ERR_CHECK(  SG_varray__count(pCtx, pvaDirtyFiles, &nrItems)  );
    if (nrItems == 1)	// if only 1 item, no fancy prompt required.
    {
        SG_vhash * pvhItem_0;			// we do not own this
        SG_uint32 iResult = 0;
        SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pvaDirtyFiles, 0, &pvhItem_0)  );
        SG_ERR_CHECK(  _do_diff1(pCtx, bWC, pOptSt, pvhItem_0, &iResult)  );

        switch (iResult)
        {
        default:
        case SG_FILETOOL__RESULT__SUCCESS:
        case SG_DIFFTOOL__RESULT__SAME:
        case SG_DIFFTOOL__RESULT__DIFFERENT:
            break;

        case SG_DIFFTOOL__RESULT__CANCEL:
        case SG_FILETOOL__RESULT__FAILURE:
        case SG_FILETOOL__RESULT__ERROR:
            nrErrors++;
            break;
        }
    }
    else
    {
        k = 0;
        while (1)
        {
            SG_vhash * pvhItem;			// we do not own this
            const char * pszRepoPath;
            char chChoice = 'd';
            SG_uint32 iResult;

            SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pvaDirtyFiles, k, &pvhItem)  );
            SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhItem, "path", &pszRepoPath)  );

            SG_ERR_CHECK(  SG_console(pCtx, SG_CS_STDOUT, "\n[%d/%d] %s:\n", k+1, nrItems, pszRepoPath)  );
            if (k == 0)
                SG_ERR_CHECK(  _do_prompt(pCtx, "(d)iff (n)ext (q)uit", "dnq", 'd', &chChoice)  );
            else if (k+1 == nrItems)
                SG_ERR_CHECK(  _do_prompt(pCtx, "(d)iff (p)rev (q)uit", "dpq", 'd', &chChoice)  );
            else
                SG_ERR_CHECK(  _do_prompt(pCtx, "(d)iff (n)ext (p)rev (q)uit", "dnpq", 'd', &chChoice)  );

            switch (chChoice)
            {
            case 'd':
                SG_ERR_CHECK(  _do_diff1(pCtx, bWC, pOptSt, pvhItem, &iResult)  );
                switch (iResult)
                {
                default:
                case SG_FILETOOL__RESULT__SUCCESS:
                case SG_DIFFTOOL__RESULT__SAME:
                case SG_DIFFTOOL__RESULT__DIFFERENT:
                    // advance to next pair of files or finish.
                    if (k+1 == nrItems)
                        goto done;
                    k++;
                    break;

                case SG_DIFFTOOL__RESULT__CANCEL:
                case SG_FILETOOL__RESULT__FAILURE:
                case SG_FILETOOL__RESULT__ERROR:
                    nrErrors++;
                    // stay on this pair of files (so that they see the
                    // error message and this filename again).
                    break;
                }
                break;

            case 'n':
                k++;
                break;

            case 'p':
                k--;
                break;

            default:
            case 'q':
                goto done;
            }
        }
    }

done:
    *pNrErrors = nrErrors;

fail:
    return;
}
Ejemplo n.º 10
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);
}
void SG_cmd_util__get_username_for_repo(
	SG_context *pCtx,
	const char *szRepoName,
	char **ppUsername
	)
{
	SG_string * pUsername = NULL;
	SG_repo * pRepo = NULL;
	char * psz_username = NULL;
	SG_curl * pCurl = NULL;
	SG_string * pUri = NULL;
	SG_string * pResponse = NULL;
	SG_int32 responseStatusCode = 0;
	SG_vhash * pRepoInfo = NULL;
	char * psz_userid = NULL;
	SG_varray * pUsers = NULL;

	SG_NULLARGCHECK_RETURN(ppUsername);

	if(!szRepoName)
	{
		// Look up username based on 'whoami' of repo associated with cwd.

		SG_ERR_IGNORE(  SG_cmd_util__get_repo_from_cwd(pCtx, &pRepo, NULL)  );
		if(pRepo)
			SG_ERR_IGNORE(  SG_user__get_username_for_repo(pCtx, pRepo, &psz_username)  );
		SG_REPO_NULLFREE(pCtx, pRepo);
	}
	else if(SG_sz__starts_with(szRepoName, "http://") || SG_sz__starts_with(szRepoName, "https://"))
	{
		// Look up username based on 'whoami' of admin id of remote repo.

		SG_ERR_CHECK(  SG_curl__alloc(pCtx, &pCurl)  );

		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, &pUri, szRepoName)  );
		SG_ERR_CHECK(  SG_string__append__sz(pCtx, pUri, ".json")  );

		SG_ERR_CHECK(  SG_curl__reset(pCtx, pCurl)  );
		SG_ERR_CHECK(  SG_curl__setopt__sz(pCtx, pCurl, CURLOPT_URL, SG_string__sz(pUri))  );

		SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &pResponse)  );
		SG_ERR_CHECK(  SG_curl__set__write_string(pCtx, pCurl, pResponse)  );

		SG_ERR_CHECK(  SG_curl__perform(pCtx, pCurl)  );
		SG_ERR_CHECK(  SG_curl__getinfo__int32(pCtx, pCurl, CURLINFO_RESPONSE_CODE, &responseStatusCode)  );

		if(responseStatusCode==200)
		{
			const char * szAdminId = NULL;
			SG_ERR_CHECK(  SG_VHASH__ALLOC__FROM_JSON__STRING(pCtx, &pRepoInfo, pResponse)  );
			SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pRepoInfo, SG_SYNC_REPO_INFO_KEY__ADMIN_ID, &szAdminId)  );

			SG_ERR_CHECK(  SG_string__clear(pCtx, pUri)  );
			SG_ERR_CHECK(  SG_string__append__format(pCtx, pUri, "/admin/%s/whoami/userid", szAdminId)  );
			SG_ERR_IGNORE(  SG_localsettings__get__sz(pCtx, SG_string__sz(pUri), NULL, &psz_userid, NULL)  );

			if(psz_userid)
			{
				// We now have the userid. Look up the username.


				SG_ERR_CHECK(  SG_string__clear(pCtx, pUri)  );
				SG_ERR_CHECK(  SG_string__append__format(pCtx, pUri, "%s/users.json", szRepoName)  );

				SG_ERR_CHECK(  SG_curl__reset(pCtx, pCurl)  );
				SG_ERR_CHECK(  SG_curl__setopt__sz(pCtx, pCurl, CURLOPT_URL, SG_string__sz(pUri))  );

				SG_ERR_CHECK(  SG_string__clear(pCtx, pResponse)  );
				SG_ERR_CHECK(  SG_curl__set__write_string(pCtx, pCurl, pResponse)  );

				SG_ERR_CHECK(  SG_curl__perform(pCtx, pCurl)  );
				SG_ERR_CHECK(  SG_curl__getinfo__int32(pCtx, pCurl, CURLINFO_RESPONSE_CODE, &responseStatusCode)  );

				if(responseStatusCode==200)
				{
					SG_uint32 i, nUsers;
					SG_ERR_CHECK(  SG_VARRAY__ALLOC__FROM_JSON__STRING(pCtx, &pUsers, pResponse)  );
					SG_ERR_CHECK(  SG_varray__count(pCtx, pUsers, &nUsers)  );
					for(i=0; i<nUsers; ++i)
					{
						SG_vhash * pUser = NULL;
						const char * psz_recid = NULL;
						SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pUsers, i, &pUser)  );
						SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pUser, "recid", &psz_recid)  );
						if(!strcmp(psz_recid, psz_userid))
						{
							const char * psz_name = NULL;
							SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pUser, "name", &psz_name)  );
							SG_ERR_CHECK(  SG_STRDUP(pCtx, psz_name, &psz_username)  );
							break;
						}
					}
					SG_VARRAY_NULLFREE(pCtx, pUsers);
				}
				
				SG_NULLFREE(pCtx, psz_userid);
			}

			SG_VHASH_NULLFREE(pCtx, pRepoInfo);
		}

		SG_STRING_NULLFREE(pCtx, pResponse);
		SG_STRING_NULLFREE(pCtx, pUri);
		SG_CURL_NULLFREE(pCtx, pCurl);
	}
	else
	{
		// Look up username based on 'whoami' of repo provided.

		SG_ERR_CHECK(  SG_REPO__OPEN_REPO_INSTANCE(pCtx, szRepoName, &pRepo)  );
		SG_ERR_IGNORE(  SG_user__get_username_for_repo(pCtx, pRepo, &psz_username)  );
		SG_REPO_NULLFREE(pCtx, pRepo);
	}

	*ppUsername = psz_username;

	return;
fail:
	SG_STRING_NULLFREE(pCtx, pUsername);
	SG_REPO_NULLFREE(pCtx, pRepo);
	SG_NULLFREE(pCtx, psz_username);
	SG_CURL_NULLFREE(pCtx, pCurl);
	SG_STRING_NULLFREE(pCtx, pUri);
	SG_STRING_NULLFREE(pCtx, pResponse);
	SG_VHASH_NULLFREE(pCtx, pRepoInfo);
	SG_NULLFREE(pCtx, psz_userid);
	SG_VARRAY_NULLFREE(pCtx, pUsers);
}
Ejemplo n.º 12
0
int u0050_logstuff_test__1(SG_context * pCtx, SG_pathname* pPathTopDir)
{
	char bufName[SG_TID_MAX_BUFFER_LENGTH];
	SG_pathname* pPathWorkingDir = NULL;
	SG_pathname* pPathFile = NULL;
	SG_vhash* pvh = NULL;
    SG_dagnode* pdn = NULL;
    const char* psz_hid_cs = NULL;
    SG_repo* pRepo = NULL;
    SG_uint32 count;
    SG_rbtree* prb = NULL;
    SG_varray* pva = NULL;
    SG_rbtree* prb_reversed = NULL;
    const char* psz_val = NULL;
    SG_audit q;

	VERIFY_ERR_CHECK(  SG_tid__generate2(pCtx, bufName, sizeof(bufName), 32)  );

	/* create the working dir */
	VERIFY_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPathWorkingDir, pPathTopDir, bufName)  );
	VERIFY_ERR_CHECK(  SG_fsobj__mkdir__pathname(pCtx, pPathWorkingDir)  );

    /* add stuff */
	VERIFY_ERR_CHECK(  u0050_logstuff__create_file__numbers(pCtx, pPathWorkingDir, "aaa", 20)  );

    /* create the repo */
	VERIFY_ERR_CHECK(  _ut_pt__new_repo(pCtx, bufName, pPathWorkingDir)  );
	VERIFY_ERR_CHECK(  _ut_pt__addremove(pCtx, pPathWorkingDir)  );
	VERIFY_ERR_CHECK(  u0050_logstuff__commit_all(pCtx, pPathWorkingDir, &pdn)  );

    VERIFY_ERR_CHECK(  SG_dagnode__get_id_ref(pCtx, pdn, &psz_hid_cs)  );

	SG_ERR_CHECK(  SG_repo__open_repo_instance(pCtx, bufName, &pRepo)  );

#define MY_COMMENT "The name of this new file sucks!  What kind of a name is 'aaa'?"

    VERIFY_ERR_CHECK(  SG_audit__init(pCtx, &q, pRepo, SG_AUDIT__WHEN__NOW, SG_AUDIT__WHO__FROM_SETTINGS)  );
    VERIFY_ERR_CHECK(  SG_vc_comments__add(pCtx, pRepo, psz_hid_cs, MY_COMMENT, &q)  );
    VERIFY_ERR_CHECK(  SG_vc_stamps__add(pCtx, pRepo, psz_hid_cs, "crap", &q)  );
    VERIFY_ERR_CHECK(  SG_vc_tags__add(pCtx, pRepo, psz_hid_cs, "tcrap", &q)  );

    VERIFY_ERR_CHECK(  SG_vc_comments__lookup(pCtx, pRepo, psz_hid_cs, &pva)  );
    VERIFY_ERR_CHECK(  SG_varray__count(pCtx, pva, &count)  );
    VERIFY_COND("count", (1 == count));
    VERIFY_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva, 0, &pvh)  );
    VERIFY_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvh, "text", &psz_val)  );
    VERIFY_COND("match", (0 == strcmp(psz_val, MY_COMMENT))  );
    SG_VARRAY_NULLFREE(pCtx, pva);

    VERIFY_ERR_CHECK(  SG_vc_stamps__lookup(pCtx, pRepo, psz_hid_cs, &pva)  );
    VERIFY_ERR_CHECK(  SG_varray__count(pCtx, pva, &count)  );
    VERIFY_COND("count", (1 == count));
    VERIFY_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva, 0, &pvh)  );
    VERIFY_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvh, "stamp", &psz_val)  );
    VERIFY_COND("match", (0 == strcmp(psz_val, "crap"))  );
    SG_VARRAY_NULLFREE(pCtx, pva);

    VERIFY_ERR_CHECK(  SG_vc_tags__lookup(pCtx, pRepo, psz_hid_cs, &pva)  );
    VERIFY_ERR_CHECK(  SG_varray__count(pCtx, pva, &count)  );
    VERIFY_COND("count", (1 == count));
    VERIFY_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva, 0, &pvh)  );
    VERIFY_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvh, "tag", &psz_val)  );
    VERIFY_COND("match", (0 == strcmp(psz_val, "tcrap"))  );
    SG_VARRAY_NULLFREE(pCtx, pva);

    VERIFY_ERR_CHECK(  SG_vc_tags__list(pCtx, pRepo, &prb)  );
    VERIFY_ERR_CHECK(  SG_rbtree__count(pCtx, prb, &count)  );
    VERIFY_COND("count", (1 == count));
    VERIFY_ERR_CHECK(  SG_vc_tags__build_reverse_lookup(pCtx, prb, &prb_reversed)  );
    VERIFY_ERR_CHECK(  SG_rbtree__count(pCtx, prb_reversed, &count)  );
    VERIFY_COND("count", (1 == count));

    {
        const char* psz_my_key = NULL;
        const char* psz_my_val = NULL;
        SG_bool b;

        VERIFY_ERR_CHECK(  SG_rbtree__iterator__first(pCtx, NULL, prb_reversed, &b, &psz_my_key, (void**) &psz_my_val)  );
        VERIFY_COND("ok", (0 == strcmp(psz_my_val, "tcrap"))  );
        VERIFY_COND("ok", (0 == strcmp(psz_my_key, psz_hid_cs))  );
    }
    SG_RBTREE_NULLFREE(pCtx, prb_reversed);

    SG_RBTREE_NULLFREE(pCtx, prb);

    VERIFY_ERR_CHECK(  SG_vc_tags__add(pCtx, pRepo, psz_hid_cs, "whatever", &q)  );

    VERIFY_ERR_CHECK(  SG_vc_tags__lookup(pCtx, pRepo, psz_hid_cs, &pva)  );
    VERIFY_ERR_CHECK(  SG_varray__count(pCtx, pva, &count)  );
    VERIFY_COND("count", (2 == count));
    SG_VARRAY_NULLFREE(pCtx, pva);

    VERIFY_ERR_CHECK(  SG_vc_tags__list(pCtx, pRepo, &prb)  );
    VERIFY_ERR_CHECK(  SG_rbtree__count(pCtx, prb, &count)  );
    VERIFY_COND("count", (2 == count));

    VERIFY_ERR_CHECK(  SG_vc_tags__build_reverse_lookup(pCtx, prb, &prb_reversed)  );
    VERIFY_ERR_CHECK(  SG_rbtree__count(pCtx, prb_reversed, &count)  );
    VERIFY_COND("count", (1 == count));

    {
        const char* psz_my_key = NULL;
        const char* psz_my_val = NULL;
        SG_bool b;

        VERIFY_ERR_CHECK(  SG_rbtree__iterator__first(pCtx, NULL, prb_reversed, &b, &psz_my_key, (void**) &psz_my_val)  );
        VERIFY_COND("ok", (0 == strcmp(psz_my_key, psz_hid_cs))  );
        /* we don't know whether psz_my_val is tcrap or whatever. */
        // VERIFY_COND("ok", (0 == strcmp(psz_my_val, "tcrap"))  );
    }
    SG_RBTREE_NULLFREE(pCtx, prb_reversed);

    SG_RBTREE_NULLFREE(pCtx, prb);

    {
        const char* psz_remove = "whatever";

        VERIFY_ERR_CHECK(  SG_vc_tags__remove(pCtx, pRepo, &q, 1, &psz_remove)  );
        /* Note that by removing whatever, we are bringing the tags list back
         * to a state where it has been before (just tcrap).  This changeset in
         * the tags table will have its own csid, because the parentage is
         * different, but it's root idtrie HID will be the same as a previous
         * node. */
    }

    VERIFY_ERR_CHECK(  SG_vc_tags__lookup(pCtx, pRepo, psz_hid_cs, &pva)  );
    VERIFY_ERR_CHECK(  SG_varray__count(pCtx, pva, &count)  );
    VERIFY_COND("count", (1 == count));
    SG_VARRAY_NULLFREE(pCtx, pva);

    VERIFY_ERR_CHECK(  SG_vc_tags__list(pCtx, pRepo, &prb)  );
    VERIFY_ERR_CHECK(  SG_rbtree__count(pCtx, prb, &count)  );
    VERIFY_COND("count", (1 == count));
    SG_RBTREE_NULLFREE(pCtx, prb);

    SG_REPO_NULLFREE(pCtx, pRepo);
    SG_DAGNODE_NULLFREE(pCtx, pdn);
	SG_PATHNAME_NULLFREE(pCtx, pPathWorkingDir);
	SG_PATHNAME_NULLFREE(pCtx, pPathFile);

	return 1;

fail:
	SG_VHASH_NULLFREE(pCtx, pvh);

	SG_PATHNAME_NULLFREE(pCtx, pPathWorkingDir);
	SG_PATHNAME_NULLFREE(pCtx, pPathFile);

	return 0;
}
void SG_vc_hooks__BROADCAST__AFTER_COMMIT(
    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_varray* pva_hooks = NULL;
    SG_vhash* pvh_params = NULL;
    char* psz_repo_id = NULL;
    char* psz_admin_id = NULL;

    SG_ERR_CHECK(  SG_vc_hooks__lookup_by_interface(
                pCtx, 
                pRepo, 
                SG_VC_HOOK__INTERFACE__BROADCAST__AFTER_COMMIT,
                &pva_hooks
                )  );
    if (pva_hooks)
    {
        SG_uint32 count_hooks = 0;
        SG_uint32 i_hook = 0;
        const char* psz_descriptor_name = 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_varray__count(pCtx, pva_hooks, &count_hooks)  );
        for (i_hook=0; i_hook<count_hooks; i_hook++)
        {
            SG_vhash* pvh_hook = NULL;
            const char* psz_js = NULL;
            const char* psz_csid = NULL;
            SG_vhash* pvh_changeset = NULL;

            SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva_hooks, i_hook, &pvh_hook)  );

            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)  );

            if (paszAssocs && count_assocs)
            {
                SG_uint32 i = 0;
                SG_varray* pva_ids = NULL;

                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, NULL)  );
            SG_VHASH_NULLFREE(pCtx, pvh_params);
        }
    }

fail:
    SG_VHASH_NULLFREE(pCtx, pvh_params);
    SG_VARRAY_NULLFREE(pCtx, pva_hooks);
	SG_NULLFREE(pCtx, psz_repo_id);
	SG_NULLFREE(pCtx, psz_admin_id);
}
void SG_vc_hooks__install(
	SG_context* pCtx,
    SG_repo* pRepo,
    const char* psz_interface,
    const char* psz_js,
	const char *module,
	SG_uint32 version,
	SG_bool replaceOld,
    const SG_audit* pq
    )
{
    char* psz_hid_cs_leaf = NULL;
    SG_zingtx* pztx = NULL;
    SG_zingrecord* prec = NULL;
    SG_dagnode* pdn = NULL;
    SG_changeset* pcs = NULL;
    SG_zingtemplate* pzt = NULL;
    SG_zingfieldattributes* pzfa = NULL;
	SG_varray *oldRecs = NULL;

	SG_UNUSED(module);
	SG_UNUSED(version);

	if (replaceOld)
	{
		SG_ERR_CHECK(  SG_vc_hooks__lookup_by_interface(pCtx, pRepo, psz_interface, &oldRecs)  );
	}

    // TODO consider validating the JS by compiling it
    SG_ERR_CHECK(  SG_zing__get_leaf(pCtx, pRepo, NULL, SG_DAGNUM__VC_HOOKS, &psz_hid_cs_leaf)  );
    
    /* start a changeset */
    SG_ERR_CHECK(  SG_zing__begin_tx(pCtx, pRepo, SG_DAGNUM__VC_HOOKS, pq->who_szUserId, psz_hid_cs_leaf, &pztx)  );
    SG_ERR_CHECK(  SG_zingtx__add_parent(pCtx, pztx, psz_hid_cs_leaf)  );

	if (replaceOld)
	{
		SG_uint32 i, count;

		SG_ERR_CHECK(  SG_varray__count(pCtx, oldRecs, &count)  );

		for ( i = 0; i < count; ++i )
		{
			const char *hidrec = NULL;
			SG_vhash *rec = NULL;

			SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, oldRecs, i, &rec)  );
			SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, rec, "hidrec", &hidrec)  );
			SG_ERR_CHECK(  SG_zingtx__delete_record__hid(pCtx, pztx, "hook", hidrec)  );
		}
	}

	SG_ERR_CHECK(  SG_zingtx__get_template(pCtx, pztx, &pzt)  );

	SG_ERR_CHECK(  SG_zingtx__create_new_record(pCtx, pztx, "hook", &prec)  );

    SG_ERR_CHECK(  SG_zingtemplate__get_field_attributes(pCtx, pzt, "hook", "interface", &pzfa)  );
    SG_ERR_CHECK(  SG_zingrecord__set_field__string(pCtx, prec, pzfa, psz_interface) );

    SG_ERR_CHECK(  SG_zingtemplate__get_field_attributes(pCtx, pzt, "hook", "js", &pzfa)  );
    SG_ERR_CHECK(  SG_zingrecord__set_field__string(pCtx, prec, pzfa, psz_js) );

	if (module)
	{
		SG_ERR_CHECK(  SG_zingtemplate__get_field_attributes(pCtx, pzt, "hook", "module", &pzfa)  );
		SG_ERR_CHECK(  SG_zingrecord__set_field__string(pCtx, prec, pzfa, module) );
	}
	if (version)
	{
		SG_ERR_CHECK(  SG_zingtemplate__get_field_attributes(pCtx, pzt, "hook", "version", &pzfa)  );
		SG_ERR_CHECK(  SG_zingrecord__set_field__int(pCtx, prec, pzfa, (SG_int64)version) );
	}

    /* commit the changes */
	SG_ERR_CHECK(  SG_zing__commit_tx(pCtx, pq->when_int64, &pztx, &pcs, &pdn, NULL)  );

    // fall thru

fail:
    if (pztx)
    {
        SG_ERR_IGNORE(  SG_zing__abort_tx(pCtx, &pztx)  );
    }
	SG_VARRAY_NULLFREE(pCtx, oldRecs);
    SG_NULLFREE(pCtx, psz_hid_cs_leaf);
    SG_DAGNODE_NULLFREE(pCtx, pdn);
    SG_CHANGESET_NULLFREE(pCtx, pcs);
}
void sg_vc_hooks__lookup_by_interface__single_result(
        SG_context* pCtx, 
        SG_repo* pRepo, 
        const char* psz_interface, 
        SG_vhash** ppvh_latest_version
        )
{
	//This version will return only the hook with the largest version.
	//If multiple versions of the hook are defined,
	//all old versions will be deleted.
	SG_varray* pva_hooks = NULL;
	SG_vhash* pvh_latest_hook = NULL;
	SG_zingtx* pztx = NULL;
	char* psz_hid_cs_leaf = NULL;
	SG_dagnode* pdn = NULL;
	SG_changeset* pcs = NULL;

	SG_ERR_CHECK(  SG_vc_hooks__lookup_by_interface(
                pCtx, 
                pRepo, 
                psz_interface,
                &pva_hooks
                )  );

	if (!pva_hooks)
    {
        SG_ERR_THROW2(  SG_ERR_VC_HOOK_MISSING,
                (pCtx, "%s", psz_interface)
                );
    }
    else
    {
        SG_uint32 count = 0;

        SG_ERR_CHECK(  SG_varray__count(pCtx, pva_hooks, &count)  );
        if (0 == count)
        {
            SG_ERR_THROW2(  SG_ERR_VC_HOOK_MISSING,
                    (pCtx, "%s", psz_interface)
                    );
        }
        else
        {
            if (count > 1)
            {
				SG_uint32 i  = 0;
				SG_int32 nVersion = 0;
				SG_int32 nHighestVersion = -1;
				SG_int32 nAmbiguousVersion = -2;
				const char * hidRecToSave = NULL;
				const char * hidrec = NULL;
				SG_vhash * pvh_current_hook = NULL;

                //There are multiple versions installed for this hook.
				//delete the lesser numbered versions.

				for (i=0; i < count; i++)
				{
					SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva_hooks, i, &pvh_current_hook)   );
					SG_ERR_CHECK(  SG_vhash__get__int32(pCtx, pvh_current_hook, "version", &nVersion)  );
					if (nVersion == nHighestVersion)
					{
						nAmbiguousVersion = nHighestVersion;
					}
					if (nVersion > nHighestVersion)
					{
						nHighestVersion = nVersion;
						SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvh_current_hook, "hidrec", &hidRecToSave)  );
					}
				}
				if (nAmbiguousVersion == nHighestVersion)
					SG_ERR_THROW2( SG_ERR_VC_HOOK_AMBIGUOUS, (pCtx, "%s defined multiple times at version %d", psz_interface, nHighestVersion) );
				if (nHighestVersion > 0 && hidRecToSave != NULL)
				{
					SG_audit q;
					SG_ERR_CHECK(  SG_audit__init(pCtx,&q,pRepo,SG_AUDIT__WHEN__NOW,SG_AUDIT__WHO__FROM_SETTINGS)  );

					SG_ERR_CHECK(  SG_zing__get_leaf(pCtx, pRepo, NULL, SG_DAGNUM__VC_HOOKS, &psz_hid_cs_leaf)  );
    
					/* start a changeset */
					SG_ERR_CHECK(  SG_zing__begin_tx(pCtx, pRepo, SG_DAGNUM__VC_HOOKS, q.who_szUserId, psz_hid_cs_leaf, &pztx)  );
					SG_ERR_CHECK(  SG_zingtx__add_parent(pCtx, pztx, psz_hid_cs_leaf)  );

					for (i=0; i < count; i++)
					{
						SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva_hooks, i, &pvh_current_hook)   );
						SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvh_current_hook, "hidrec", &hidrec)  );
						if (SG_strcmp__null(hidrec, hidRecToSave) != 0)
						{
							//This isn't the recid to save.  Delete it!
							SG_ERR_CHECK(  SG_zingtx__delete_record__hid(pCtx, pztx, "hook", hidrec)  );
						}
					}

					/* commit the changes */
					SG_ERR_CHECK(  SG_zing__commit_tx(pCtx, q.when_int64, &pztx, &pcs, &pdn, NULL)  );
				}
            }
			else
			{
				//There's only one hook installed return it.
				SG_vhash * pvh_temp = NULL;
				SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, pva_hooks, 0, &pvh_temp)  );
				SG_ERR_CHECK( SG_VHASH__ALLOC__COPY(pCtx, &pvh_latest_hook, pvh_temp)  );
			}
		}
	}
	SG_RETURN_AND_NULL(pvh_latest_hook, ppvh_latest_version);

fail:
	if (pztx)
    {
        SG_ERR_IGNORE(  SG_zing__abort_tx(pCtx, &pztx)  );
    }
	SG_NULLFREE(pCtx, psz_hid_cs_leaf);
	SG_DAGNODE_NULLFREE(pCtx, pdn);
	SG_CHANGESET_NULLFREE(pCtx, pcs);
	SG_VARRAY_NULLFREE(pCtx, pva_hooks);

}
Ejemplo n.º 16
0
static void _fillInUserSelection(
	SG_context *pCtx,
	SG_string *pstrRepoDescriptorName,
	SG_string *replacement)
{
	SG_repo *repo = NULL;
	SG_varray *users = NULL;
	SG_vhash *user = NULL;
	SG_uint32 i = 0;
	SG_uint32 count;
	SG_string *semail = NULL;
	SG_string *suid = NULL;
	SG_string *entry = NULL;
	SG_string *curuid = NULL;

	SG_ERR_CHECK(  SG_string__clear(pCtx, replacement)  );
	SG_ERR_CHECK(  SG_repo__open_repo_instance(pCtx, SG_string__sz(pstrRepoDescriptorName), &repo)  );

	SG_ERR_CHECK(  SG_user__list_all(pCtx, repo, &users)  );
	SG_ERR_CHECK(  SG_varray__count(pCtx, users, &count)  );

	SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &semail)  );
	SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &suid)  );
	SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &entry)  );

	SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &curuid)  );
	SG_ERR_CHECK(  _getUserId(pCtx, repo, curuid)  );

	for ( i = 0; i < count; ++i )
	{
		const char *uid = NULL;
		const char *email = NULL;
		const char *selected = NULL;

		SG_ERR_CHECK(  SG_varray__get__vhash(pCtx, users, i, &user)  );

		SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, user, "recid", &uid)  );
		SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, user, "email", &email)  );

		SG_ERR_CHECK(  _getEncoded(pCtx, uid, suid)  );
		SG_ERR_CHECK(  _getEncoded(pCtx, email, semail)  );

		if (eq(SG_string__sz(curuid), uid))
		{
			selected = " selected='selected' ";
		}
		else
		{
			selected = "";
		}

		SG_ERR_CHECK(  SG_string__sprintf(pCtx, entry, "<option value=\"%s\" %s>%s</option>",
											SG_string__sz(suid), selected, SG_string__sz(semail))  );

		SG_ERR_CHECK(  SG_string__append__string(pCtx, replacement, entry)  );
	}
fail:
	SG_VARRAY_NULLFREE(pCtx, users);
	SG_REPO_NULLFREE(pCtx, repo);
	SG_STRING_NULLFREE(pCtx, semail);
	SG_STRING_NULLFREE(pCtx, suid);
	SG_STRING_NULLFREE(pCtx, entry);
	SG_STRING_NULLFREE(pCtx, curuid);
}