void SG_sync_remote__heartbeat(
	SG_context* pCtx,
	SG_repo* pRepo,
	SG_vhash** ppvh)
{
	SG_vhash* pvh = NULL;
	char* pszRepoId = NULL;
	char* pszAdminId = NULL;
	char* pszHashMethod = NULL;

	SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvh)  );
	
	SG_ERR_CHECK(  SG_repo__get_repo_id(pCtx, pRepo, &pszRepoId)  );
	SG_ERR_CHECK(  SG_repo__get_admin_id(pCtx, pRepo, &pszAdminId)  );
	SG_ERR_CHECK(  SG_repo__get_hash_method(pCtx, pRepo, &pszHashMethod)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__REPO_ID, pszRepoId)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__ADMIN_ID, pszAdminId)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__HASH_METHOD, pszHashMethod)  );

	*ppvh = pvh;
	pvh = NULL;

	/* fall through */
fail:
	SG_VHASH_NULLFREE(pCtx, pvh);
	SG_NULLFREE(pCtx, pszRepoId);
	SG_NULLFREE(pCtx, pszAdminId);
	SG_NULLFREE(pCtx, pszHashMethod);

}
示例#2
0
void u0038_test_wdmapping(SG_context * pCtx)
{
	SG_vhash* pvh = NULL;
	SG_pathname* pPath = NULL;
	SG_pathname* pMappedPath = NULL;
	SG_string* pstrRepoDescriptorName = NULL;
	char* pszidGid = NULL;

	VERIFY_ERR_CHECK_DISCARD(  SG_PATHNAME__ALLOC(pCtx, &pPath)  );

	VERIFY_ERR_CHECK_DISCARD(  SG_pathname__set__from_cwd(pCtx, pPath)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_VHASH__ALLOC(pCtx, &pvh)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_vhash__add__string__sz(pCtx, pvh, "hello", "world")  );
	VERIFY_ERR_CHECK_DISCARD(  SG_vhash__add__string__sz(pCtx, pvh, "hola", "mundo")  );
	SG_ERR_IGNORE(  SG_closet__descriptors__remove(pCtx, "r1")  );
	VERIFY_ERR_CHECK_DISCARD(  SG_closet__descriptors__add(pCtx, "r1", pvh)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_workingdir__set_mapping(pCtx, pPath, "r1", NULL)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_workingdir__find_mapping(pCtx, pPath, &pMappedPath, &pstrRepoDescriptorName, &pszidGid)  );
	VERIFY_COND("ridesc match", (0 == strcmp("r1", SG_string__sz(pstrRepoDescriptorName))));
	VERIFY_ERR_CHECK_DISCARD(  SG_pathname__append__from_sz(pCtx, pPath, "foo")  );
	VERIFY_ERR_CHECK_DISCARD(  SG_pathname__append__from_sz(pCtx, pPath, "bar")  );
	VERIFY_ERR_CHECK_DISCARD(  SG_pathname__append__from_sz(pCtx, pPath, "plok")  );

	SG_STRING_NULLFREE(pCtx, pstrRepoDescriptorName);
	SG_PATHNAME_NULLFREE(pCtx, pMappedPath);

	VERIFY_ERR_CHECK_DISCARD(  SG_workingdir__find_mapping(pCtx, pPath, &pMappedPath, &pstrRepoDescriptorName, &pszidGid)  );

	SG_STRING_NULLFREE(pCtx, pstrRepoDescriptorName);
	SG_PATHNAME_NULLFREE(pCtx, pMappedPath);
	SG_PATHNAME_NULLFREE(pCtx, pPath);

	SG_VHASH_NULLFREE(pCtx, pvh);
}
示例#3
0
void SG_workingdir__set_mapping(
	SG_context* pCtx,
	const SG_pathname* pPathLocalDirectory,
	const char* pszNameRepoInstanceDescriptor, /**< The name of the repo instance descriptor */
	const char* pszidGidAnchorDirectory /**< The GID of the directory within the repo to which this is anchored.  Usually it's user root. */
	)
{
	SG_vhash* pvhNew = NULL;
	SG_vhash* pvh = NULL;
	SG_pathname* pMyPath = NULL;
	SG_pathname* pDrawerPath = NULL;
	SG_pathname* pMappingFilePath = NULL;

	SG_NULLARGCHECK_RETURN(pPathLocalDirectory);
	SG_NULLARGCHECK_RETURN(pszNameRepoInstanceDescriptor);

	/* make a copy of the path so we can modify it (adding the final slash) */
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pMyPath, pPathLocalDirectory)  );

	/* null the original parameter pointer to make sure we don't use it anymore */
	pPathLocalDirectory = NULL;

	/* make sure the path we were given is a directory that exists */
	SG_ERR_CHECK(  SG_fsobj__verify_directory_exists_on_disk__pathname(pCtx, pMyPath)  );

	/* it's a directory, so it should have a final slash */
	SG_ERR_CHECK(  SG_pathname__add_final_slash(pCtx, pMyPath)  );

	/* make sure the name of the repo instance descriptor is valid */
	SG_ERR_CHECK(  SG_closet__descriptors__get(pCtx, pszNameRepoInstanceDescriptor, &pvh) );
	SG_VHASH_NULLFREE(pCtx, pvh);

	// TODO verify that the anchor GID is valid for that repo?

	SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhNew)  );

	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhNew, "descriptor", pszNameRepoInstanceDescriptor)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhNew, "anchor", pszidGidAnchorDirectory)  );

	SG_ERR_CHECK(  SG_workingdir__verify_drawer_exists(pCtx, pMyPath, &pDrawerPath)  );
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pMappingFilePath, pDrawerPath, "repo.json")  );

	SG_ERR_CHECK(  SG_vfile__update__vhash(pCtx, pMappingFilePath, "mapping", pvhNew)  );

	SG_VHASH_NULLFREE(pCtx, pvhNew);
	SG_PATHNAME_NULLFREE(pCtx, pMyPath);
	SG_PATHNAME_NULLFREE(pCtx, pDrawerPath);
	SG_PATHNAME_NULLFREE(pCtx, pMappingFilePath);

	return;
fail:
	SG_PATHNAME_NULLFREE(pCtx, pDrawerPath);
	SG_PATHNAME_NULLFREE(pCtx, pMappingFilePath);
	SG_PATHNAME_NULLFREE(pCtx, pMyPath);
	SG_VHASH_NULLFREE(pCtx, pvhNew);
	SG_VHASH_NULLFREE(pCtx, pvh);
}
/**
 * Select an appropriate difftool to use on this file.
 * We require that the common and right-side fields of
 * pvhDiffItem be filled in first.  (The left-side fields
 * can be after this is called.)
 *
 */
static void _pick_tool(SG_context * pCtx,
					   sg_wc6diff__setup_data * pData,
					   const SG_vhash * pvhItem,
					   SG_vhash * pvhDiffItem,
					   SG_string * pStringHeader)
{
	SG_filetool * pTool = NULL;
	const char * pszRepoPath_live = NULL;
	const char * pszAbsPath = NULL;
	SG_pathname * pPathname = NULL;
	const char * pszToolNameChosen = NULL;
	SG_bool bIsTmp_right = SG_FALSE;

	// use the suffix on the (always present) live-repo-path (rather than
	// the left- or right- version) to pick a tool.
	// 
	// if that fails, use the suffix of current file in the WD
	// (provided it isn't a temp file).  the left side is always
	// a temp file, so don't bother with it.

	SG_ERR_CHECK(  SG_vhash__check__sz(pCtx, pvhItem, "path", &pszRepoPath_live)  );
	SG_ERR_CHECK(  SG_vhash__check__bool(pCtx, pvhDiffItem, "right_is_temp", &bIsTmp_right)  );
	if (!bIsTmp_right)
	{
		SG_ERR_CHECK(  SG_vhash__check__sz(pCtx, pvhDiffItem, "right_abs_path", &pszAbsPath)  );
		if (pszAbsPath)
			SG_ERR_CHECK(  SG_PATHNAME__ALLOC__SZ(pCtx, &pPathname, pszAbsPath)  );
	}

	SG_ERR_CHECK(  SG_difftool__select(pCtx,
									   pData->pszDiffToolContext, // gui or console
									   pszRepoPath_live,          // current/live repo-path
									   pPathname,                 // live or temp absolute pathname
									   pData->pszTool,            // suggested/requested tool
									   pData->pWcTx->pDb->pRepo,
									   &pTool)  );
	if (pTool)
	{
		SG_ERR_CHECK(  SG_filetool__get_name(pCtx, pTool, &pszToolNameChosen)  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "tool", pszToolNameChosen)  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "context", pData->pszDiffToolContext)  );
	}
	else
	{
		SG_ERR_CHECK(  SG_string__append__format(pCtx, pStringHeader,
												 "=== No difftool configured for ('%s', '%s') in context '%s'.\n",
												 pszRepoPath_live, pszAbsPath, pData->pszDiffToolContext)  );
	}

fail:
	SG_FILETOOL_NULLFREE(pCtx, pTool);
	SG_PATHNAME_NULLFREE(pCtx, pPathname);
}
/**
 * For MODIFIED or DELETED items we need to populate the left side
 * of the diff.  This should be called after _pick_tool().
 * 
 * put the various fields that we need to use in the call to SG_difftool__run()
 * into the pvhDiffItem for later.
 *
 */
static void _get_left_side_details(SG_context * pCtx,
								   sg_wc6diff__setup_data * pData,
								   const SG_vhash * pvhItem,
								   SG_vhash * pvhDiffItem)
{
	SG_string * pStringLabel_left  = NULL;
	SG_pathname * pPathAbsolute_left = NULL;
	SG_vhash * pvhSubsection_left = NULL;		// we do not own this
	const char * pszRepoPath_left;
	const char * pszGid;
	const char * pszHid_left;
	const char * pszToolName = NULL;
	const char * pszName_left;

	// get the repo-path for the left side *AS IT EXISTED IN THE LEFT CSET*.
	SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhItem, pData->pszSubsectionLeft, &pvhSubsection_left)  );
	SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhSubsection_left, "hid", &pszHid_left)  );
	SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhSubsection_left, "path", &pszRepoPath_left)  );
	SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhSubsection_left, "name", &pszName_left)  );

	// left label is "<left_repo-path> <hid>" (we have an HID and since no date makes sense)
	SG_ERR_CHECK(  sg_wc_diff_utils__make_label(pCtx, pszRepoPath_left, pszHid_left, NULL, &pStringLabel_left)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "left_label",    SG_string__sz(pStringLabel_left))  );

	SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhDiffItem, "tool", &pszToolName)  );
	if (strcmp(pszToolName, SG_DIFFTOOL__INTERNAL__SKIP) == 0)
	{
		// There's no point in exporting a binary file into TEMP so
		// that we can invoke a no-op difftool.
		// See W5937.
	}
	else
	{
		// fetch the baseline version of the file into a temp file.
		// the left side should be read-only because it refers to a
		// historical version (regardless of whether or not we are
		// interactive).

		SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhItem, "gid", &pszGid)  );
		SG_ERR_CHECK(  sg_wc_diff_utils__export_to_temp_file(pCtx, pData->pWcTx, pData->pszSubsectionLeft,
															 pszGid, pszHid_left,
															 pszName_left,
															 &pPathAbsolute_left)  );
		SG_ERR_CHECK(  SG_fsobj__chmod__pathname(pCtx, pPathAbsolute_left, 0400)  );

		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "left_abs_path", SG_pathname__sz(pPathAbsolute_left))  );
	}

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPathAbsolute_left);
	SG_STRING_NULLFREE(pCtx, pStringLabel_left);
}
void u0025_vhash__create(SG_context * pCtx, SG_vhash** pph)
{
	SG_vhash* ph = NULL;
	SG_varray* pa = NULL;
	SG_vhash* pvhSub = NULL;
	SG_varray* pvaSub = NULL;

	SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &ph)  );

	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, ph, "hello", "world")  );

	SG_ERR_CHECK(  SG_VARRAY__ALLOC(pCtx, &pa)  );

	SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pa, 31)  );

	SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pa, 51)  );

	SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhSub)  );

	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhSub, "not", "now")  );

	SG_ERR_CHECK(  SG_varray__append__vhash(pCtx, pa, &pvhSub)  );

	SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pa, 71)  );

	SG_ERR_CHECK(  SG_VARRAY__ALLOC(pCtx, &pvaSub)  );

	SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pvaSub, 81)  );

	SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pvaSub, 82)  );

	SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pvaSub, 83)  );

	SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pvaSub, 84)  );

	SG_ERR_CHECK(  SG_varray__append__varray(pCtx, pa, &pvaSub)  );

	SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pa, 91)  );

	SG_ERR_CHECK(  SG_vhash__add__varray(pCtx, ph, "a", &pa)  );

	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, ph, "b", "fiddle")  );

	*pph = ph;

	return;

fail:
	// TODO free
	return;
}
void SG_vc_hooks__ASK__WIT__LIST_ITEMS(
    SG_context* pCtx, 
    SG_repo* pRepo,
	const char * psz_search_term,
	SG_varray *pBugs
	)
{
	SG_vhash* pvh_params = NULL;
    SG_vhash* pvh_result = NULL;
	SG_vhash* pvh_hook = NULL;
	const char* psz_js = NULL;
	const char* psz_descriptor_name = NULL;
	SG_bool hasBugs = SG_FALSE;

	SG_ERR_CHECK(  sg_vc_hooks__lookup_by_interface__single_result(
                pCtx, 
                pRepo, 
                SG_VC_HOOK__INTERFACE__ASK__WIT__LIST_ITEMS,
                &pvh_hook
                )  );

    if (!pvh_hook)
		return;

	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_repo__get_descriptor_name(pCtx, pRepo, &psz_descriptor_name)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh_params, "descriptor_name", psz_descriptor_name)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh_params, "text", psz_search_term)  );

	SG_ERR_CHECK(  SG_vc_hooks__execute(pCtx, psz_js, pvh_params, &pvh_result)  );

	SG_ERR_CHECK(  SG_vhash__has(pCtx, pvh_result, "items", &hasBugs)  );

	if (hasBugs && pBugs)
	{
		SG_varray *bugs = NULL;
		SG_ERR_CHECK(  SG_vhash__get__varray(pCtx, pvh_result, "items", &bugs)  );

		SG_ERR_CHECK(  SG_varray__copy_items(pCtx, bugs, pBugs)  );
	}
fail:
	SG_VHASH_NULLFREE(pCtx, pvh_params);
    SG_VHASH_NULLFREE(pCtx, pvh_result);
    SG_VHASH_NULLFREE(pCtx, pvh_hook);
}
示例#8
0
void SG_dbrecord__add_pair(SG_context* pCtx, SG_dbrecord* prec, const char* putf8Name, const char* putf8Value)
{
	if (prec->pid)
    {
		SG_ERR_THROW_RETURN(SG_ERR_INVALID_WHILE_FROZEN);
    }
	SG_ERR_CHECK_RETURN(  SG_vhash__add__string__sz(pCtx, prec->pvh, putf8Name, putf8Value)  );
}
示例#9
0
void SG_dagfrag__to_vhash__shared(SG_context * pCtx,
								  SG_dagfrag * pFrag,
								  SG_vhash * pvhShared,
								  SG_vhash ** ppvhNew)
{
	SG_vhash * pvhFrag = NULL;
	SG_varray * pvaMyData = NULL;
	struct _serialize_data serialize_data;

	SG_NULLARGCHECK_RETURN(pFrag);
	SG_NULLARGCHECK_RETURN(ppvhNew);

	SG_ASSERT(  (! IS_TRANSIENT(pFrag))  );

	SG_ERR_CHECK(  SG_VHASH__ALLOC__SHARED(pCtx,&pvhFrag,5,pvhShared)  );

	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx,pvhFrag,KEY_REPO_ID,pFrag->m_sz_repo_id)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx,pvhFrag,KEY_ADMIN_ID,pFrag->m_sz_admin_id)  );
	SG_ERR_CHECK(  SG_vhash__add__int64(pCtx,pvhFrag,KEY_DAGNUM,(SG_int64)pFrag->m_iDagNum)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx,pvhFrag,KEY_VERSION,VALUE_VERSION)  );

	SG_ERR_CHECK(  SG_VARRAY__ALLOC(pCtx,&pvaMyData)  );

	serialize_data.pvhFrag = pvhFrag;
	serialize_data.pvaMyData = pvaMyData;

	// walk the complete RB_Cache and add complete info for each my_data item
	// (regardless of whether the dagnode is a START or INTERIOR member or in
	// the END-FRINGE.  These will be in an essentially random order (HID).

	SG_ERR_CHECK(  SG_rbtree__foreach(pCtx,
									  pFrag->m_pRB_Cache,
									  _serialize_data_cb,
									  &serialize_data)  );

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

	*ppvhNew = pvhFrag;
	return;

fail:
	SG_VHASH_NULLFREE(pCtx, pvhFrag);
	SG_VARRAY_NULLFREE(pCtx, pvaMyData);
}
示例#10
0
void MyFn(create_repo)(SG_context * pCtx, SG_repo ** ppRepo)
{
	// caller must free returned value.

	SG_repo * pRepo;
	SG_pathname * pPathnameRepoDir = NULL;
	SG_vhash* pvhPartialDescriptor = NULL;
    char buf_repo_id[SG_GID_BUFFER_LENGTH];
    char buf_admin_id[SG_GID_BUFFER_LENGTH];
	char* pszRepoImpl = NULL;

	VERIFY_ERR_CHECK(  SG_gid__generate(pCtx, buf_repo_id, sizeof(buf_repo_id))  );
	VERIFY_ERR_CHECK(  SG_gid__generate(pCtx, buf_admin_id, sizeof(buf_admin_id))  );

	VERIFY_ERR_CHECK(  SG_PATHNAME__ALLOC(pCtx, &pPathnameRepoDir)  );
	VERIFY_ERR_CHECK(  SG_pathname__set__from_cwd(pCtx, pPathnameRepoDir)  );

	VERIFY_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhPartialDescriptor)  );

	VERIFY_ERR_CHECK_DISCARD(  SG_localsettings__get__sz(pCtx, SG_LOCALSETTING__NEWREPO_DRIVER, NULL, &pszRepoImpl, NULL)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_vhash__add__string__sz(pCtx, pvhPartialDescriptor, SG_RIDESC_KEY__STORAGE, pszRepoImpl)  );

	VERIFY_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhPartialDescriptor, SG_RIDESC_FSLOCAL__PATH_PARENT_DIR, SG_pathname__sz(pPathnameRepoDir))  );

	VERIFY_ERR_CHECK(  SG_repo__create_repo_instance(pCtx,pvhPartialDescriptor,SG_TRUE,NULL,buf_repo_id,buf_admin_id,&pRepo)  );

	SG_VHASH_NULLFREE(pCtx, pvhPartialDescriptor);

	{
		const SG_vhash * pvhRepoDescriptor = NULL;
		VERIFY_ERR_CHECK(  SG_repo__get_descriptor(pCtx, pRepo,&pvhRepoDescriptor)  );

		//INFOP("open_repo",("Repo is [%s]",SG_string__sz(pstrRepoDescriptor)));
	}

	*ppRepo = pRepo;

fail:
	SG_VHASH_NULLFREE(pCtx, pvhPartialDescriptor);
	SG_PATHNAME_NULLFREE(pCtx, pPathnameRepoDir);

    SG_NULLFREE(pCtx, pszRepoImpl);
}
void MyFn(create_repo)(SG_context * pCtx, SG_repo** ppRepo)
{
	SG_repo* pRepo = NULL;
	SG_pathname* pPath_repo = NULL;
	char buf_repo_id[SG_GID_BUFFER_LENGTH];
	char buf_admin_id[SG_GID_BUFFER_LENGTH];
	SG_vhash* pvhPartialDescriptor = NULL;
	char* pszRepoImpl = NULL;

	VERIFY_ERR_CHECK(  SG_gid__generate(pCtx, buf_repo_id, sizeof(buf_repo_id))  );
	VERIFY_ERR_CHECK(  SG_gid__generate(pCtx, buf_admin_id, sizeof(buf_admin_id))  );

	/* Get our paths fixed up */
	VERIFY_ERR_CHECK(  SG_PATHNAME__ALLOC(pCtx, &pPath_repo)  );
	VERIFY_ERR_CHECK(  SG_pathname__set__from_cwd(pCtx, pPath_repo)  );
	VERIFY_ERR_CHECK(  SG_pathname__append__from_sz(pCtx, pPath_repo, "repo")  );

	SG_fsobj__mkdir__pathname(pCtx, pPath_repo);
	SG_context__err_reset(pCtx);

	// Create the repo
	VERIFY_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhPartialDescriptor)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_localsettings__get__sz(pCtx, SG_LOCALSETTING__NEWREPO_DRIVER, NULL, &pszRepoImpl, NULL)  );
    if (pszRepoImpl)
    {
        VERIFY_ERR_CHECK_DISCARD(  SG_vhash__add__string__sz(pCtx, pvhPartialDescriptor, SG_RIDESC_KEY__STORAGE, pszRepoImpl)  );
    }

	VERIFY_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhPartialDescriptor, SG_RIDESC_FSLOCAL__PATH_PARENT_DIR, SG_pathname__sz(pPath_repo))  );
	VERIFY_ERR_CHECK(  SG_repo__create_repo_instance(pCtx,NULL,pvhPartialDescriptor,SG_TRUE,NULL,buf_repo_id,buf_admin_id,&pRepo)  );

	*ppRepo = pRepo;

	// Fall through to common cleanup

fail:
	SG_VHASH_NULLFREE(pCtx, pvhPartialDescriptor);
	SG_PATHNAME_NULLFREE(pCtx, pPath_repo);

    SG_NULLFREE(pCtx, pszRepoImpl);
}
示例#12
0
void u0038_test_ridesc(SG_context * pCtx)
{
	SG_vhash* pvh = NULL;
	SG_vhash* pvh2 = NULL;
	SG_vhash* pvh_all = NULL;
	SG_uint32 count = 0;

	VERIFY_ERR_CHECK_DISCARD(  SG_VHASH__ALLOC(pCtx, &pvh)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_vhash__add__string__sz(pCtx, pvh, "hello", "world")  );
	VERIFY_ERR_CHECK_DISCARD(  SG_vhash__add__string__sz(pCtx, pvh, "hola", "mundo")  );
	SG_ERR_IGNORE(  SG_closet__descriptors__remove(pCtx, "1")  );
	VERIFY_ERR_CHECK_DISCARD(  SG_closet__descriptors__add(pCtx, "1", pvh)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_closet__descriptors__remove(pCtx, "1")  );

	SG_ERR_IGNORE(  SG_closet__descriptors__remove(pCtx, "2")  );		/* This may or may not be an error */

	/* delete one that is not there should be an error */
	VERIFY_ERR_CHECK_HAS_ERR_DISCARD(  SG_closet__descriptors__remove(pCtx, "2")  );

	/* fetch one that is not there should be an error */
	VERIFY_ERR_CHECK_HAS_ERR_DISCARD(  SG_closet__descriptors__get(pCtx, "2", &pvh2)  );

	SG_ERR_IGNORE(  SG_closet__descriptors__remove(pCtx, "3")  );
	SG_ERR_IGNORE(  SG_closet__descriptors__remove(pCtx, "4")  );
	VERIFY_ERR_CHECK_DISCARD(  SG_closet__descriptors__add(pCtx, "3", pvh)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_closet__descriptors__add(pCtx, "4", pvh)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_closet__descriptors__get(pCtx, "3", &pvh2)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_closet__descriptors__list(pCtx, &pvh_all)  );
	VERIFY_ERR_CHECK_DISCARD(  SG_vhash__count(pCtx, pvh_all, &count)  );
	VERIFY_COND("count", (count >= 2));
	SG_ERR_IGNORE(  SG_closet__descriptors__remove(pCtx, "3")  );
	SG_ERR_IGNORE(  SG_closet__descriptors__remove(pCtx, "4")  );

	SG_VHASH_NULLFREE(pCtx, pvh_all);
	SG_VHASH_NULLFREE(pCtx, pvh);
	SG_VHASH_NULLFREE(pCtx, pvh2);
}
/**
 * For MODIFIED or ADDED items we need to populate the right side
 * of the diff.
 *
 * See footnote 1 above for bIsTmp_right.
 * 
 */
static void _get_right_side_details(SG_context * pCtx,
									sg_wc6diff__setup_data * pData,
									const SG_vhash * pvhItem,
									sg_wc_liveview_item * pLVI,
									SG_vhash * pvhDiffItem)
{
	SG_string * pStringLabel_right  = NULL;
	SG_pathname * pPathAbsolute_right = NULL;
	SG_vhash * pvhSubsection_right = NULL;		// we do not own this
	const char * pszRepoPath_right;
	char bufDate[SG_TIME_FORMAT_LENGTH+1];
	SG_bool bIsTmp_right;
	SG_fsobj_stat st_right;

	// get the repo-path for the right side *AS IT EXISTED IN THE RIGHT CSET*.
	SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhItem, pData->pszSubsectionRight, &pvhSubsection_right)  );
	SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhSubsection_right, "path", &pszRepoPath_right)  );

	SG_ERR_CHECK(  sg_wc_liveview_item__get_proxy_file_path(pCtx, pLVI, pData->pWcTx, &pPathAbsolute_right, &bIsTmp_right)  );
	SG_ERR_CHECK(  SG_fsobj__stat__pathname(pCtx, pPathAbsolute_right, &st_right)  );
	SG_ERR_CHECK(  SG_time__format_utc__i64(pCtx, st_right.mtime_ms, bufDate, SG_NrElements(bufDate))  );

	// the right label is "<right_repo_path> <datestamp>"
	SG_ERR_CHECK(  SG_fsobj__stat__pathname(pCtx, pPathAbsolute_right, &st_right)  );
	SG_ERR_CHECK(  sg_wc_diff_utils__make_label(pCtx, pszRepoPath_right, NULL, bufDate, &pStringLabel_right)  );

	// put the various fields that we need to use in the call to SG_difftool__run()
	// into the pvhDiffItem for later.

	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "right_label",    SG_string__sz(pStringLabel_right))  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "right_abs_path", SG_pathname__sz(pPathAbsolute_right))  );
	SG_ERR_CHECK(  SG_vhash__add__bool(      pCtx, pvhDiffItem, "right_is_tmp",   bIsTmp_right)  );

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPathAbsolute_right);
	SG_STRING_NULLFREE(pCtx, pStringLabel_right);
}
示例#14
0
void SG_localsettings__factory__list__vhash(
    SG_context* pCtx,
    SG_vhash** ppvh
    )
{
    SG_uint32 i = 0;
    SG_vhash* pvh = NULL;
    SG_varray* pva = NULL;

    SG_ERR_CHECK(  SG_vhash__alloc(pCtx, &pvh)  );
    for (i=0; i<(sizeof(g_factory_defaults) / sizeof(struct localsetting_factory_default)); i++)
    {
        if (SG_VARIANT_TYPE_SZ == g_factory_defaults[i].type)
        {
            SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh, g_factory_defaults[i].psz_path_partial, g_factory_defaults[i].psz_val)  );
        }
        else if (SG_VARIANT_TYPE_VARRAY == g_factory_defaults[i].type)
        {
            const char** pp_el = NULL;

            SG_ERR_CHECK(  SG_varray__alloc(pCtx, &pva)  );

            pp_el = g_factory_defaults[i].pasz_array;
            while (*pp_el)
            {
                SG_ERR_CHECK(  SG_varray__append__string__sz(pCtx, pva, *pp_el)  );
                pp_el++;
            }
            SG_ERR_CHECK(  SG_vhash__add__varray(pCtx, pvh, g_factory_defaults[i].psz_path_partial, &pva)  );
        }
        else
        {
            SG_ERR_THROW(  SG_ERR_NOTIMPLEMENTED  );
        }

        }

    *ppvh = pvh;
    pvh = NULL;

fail:
    SG_VHASH_NULLFREE(pCtx, pvh);
}
/**
 * A DELETED file is present in the baseline and is not present in the WD.
 * Let the difftool have a generated TEMPFILE for the historical left side
 * and a NULL for the right side file.
 *
 * We expect that the difftool will simply print the entire baseline file
 * with '<' or '-' in front of each line.
 *
 */
static void _diff__file__deleted(SG_context * pCtx,
								 sg_wc6diff__setup_data * pData,
								 const SG_vhash * pvhItem,
								 SG_vhash * pvhDiffItem,
								 SG_string * pStringHeader)
{
	SG_string * pStringLabel_right = NULL;

	// the file does not exist in the right cset, so any repo-path label
	// we make for the right side is a bit bogus.
	SG_ERR_CHECK(  sg_wc_diff_utils__make_label(pCtx, "(not present)", NULL, NULL, &pStringLabel_right)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "right_label", SG_string__sz(pStringLabel_right))  );

	SG_ERR_CHECK(  _pick_tool(pCtx, pData, pvhItem, pvhDiffItem, pStringHeader)  );
	SG_ERR_CHECK(  _get_left_side_details(pCtx, pData, pvhItem, pvhDiffItem)  );

fail:
	SG_STRING_NULLFREE(pCtx, pStringLabel_right);
}
示例#16
0
static void _serialize_data_cb(SG_context * pCtx,
							   const char * szHid,
							   void * pAssocData,
							   void * pVoidSerializeData)
{
	struct _serialize_data * pSerializeData = (struct _serialize_data *)pVoidSerializeData;
	_my_data * pMyData = (_my_data *)pAssocData;

	SG_vhash * pvhMyData = NULL;
	SG_vhash * pvhDagnode = NULL;

	SG_ERR_CHECK(  SG_VHASH__ALLOC__SHARED(pCtx,&pvhMyData,5,pSerializeData->pvhFrag)  );

	SG_ERR_CHECK(  SG_vhash__add__int64(pCtx,pvhMyData,KEY_DFS_STATE,(SG_int64)pMyData->m_state)  );

    if (SG_DFS_END_FRINGE == pMyData->m_state)
    {
        SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx,pvhMyData,KEY_DAGNODE_ID,szHid)  );
    }
    else
    {
        SG_ERR_CHECK(  SG_dagnode__to_vhash__shared(pCtx,pMyData->m_pDagnode,pvhMyData,&pvhDagnode)  );
        SG_ERR_CHECK(  SG_vhash__add__vhash(pCtx,pvhMyData,KEY_ACTUAL_DAGNODE,&pvhDagnode)  );
    }

#if DEBUG && TRACE_DAGFRAG && 0
	SG_ERR_CHECK(  SG_vhash_debug__dump_to_console(pCtx, pvhMyData)  );
#endif
	SG_ERR_CHECK(  SG_varray__append__vhash(pCtx,pSerializeData->pvaMyData,&pvhMyData)  );

	return;

fail:
	SG_VHASH_NULLFREE(pCtx, pvhMyData);
	SG_VHASH_NULLFREE(pCtx, pvhDagnode);
}
void sg_wc_tx__diff__setup__file(SG_context * pCtx,
								 sg_wc6diff__setup_data * pData,
								 const SG_vhash * pvhItem,
								 SG_wc_status_flags statusFlags)
{
	SG_string * pStringHeader = NULL;
	SG_vhash * pvhDiffItem = NULL;		// we do not own this
	const char * pszGid;				// we do not own this
	const char * pszLiveRepoPath;		// we do not own this
	SG_vhash * pvhItemStatus;			// we do not own this

	if (statusFlags & SG_WC_STATUS_FLAGS__U__IGNORED)
	{
		// The code in wc4status won't create header info for ignored
		// items unless verbose is set and diff never sets it, so we
		// skip this item to avoid getting the divider row of equal signs.

		return;
	}

	SG_ERR_CHECK(  SG_varray__appendnew__vhash(pCtx, pData->pvaDiffSteps, &pvhDiffItem)  );
	SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhItem, "gid", &pszGid)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "gid", pszGid)  );
	SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhItem, "path", &pszLiveRepoPath)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "path", pszLiveRepoPath)  );
	SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhItem, "status", &pvhItemStatus)  );
	SG_ERR_CHECK(  SG_vhash__addcopy__vhash(pCtx, pvhDiffItem, "status", pvhItemStatus)  );

	// the following if-else-if-else... DOES NOT imply that these statuses
	// are mutually exclusive (for example, one could have ADDED+LOST), but
	// rather just the cases when we actually want to print the content diffs.

	if (statusFlags & SG_WC_STATUS_FLAGS__A__SPARSE)
	{
		SG_ERR_CHECK(  sg_wc_tx__diff__setup__header(pCtx, pvhItem, &pStringHeader)  );
		SG_ERR_CHECK(  SG_string__append__format(pCtx, pStringHeader, "=== %s\n", "Omitting details for sparse item.")  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "header", SG_string__sz(pStringHeader))  );
	}
	else if (statusFlags & SG_WC_STATUS_FLAGS__U__LOST)
	{
		SG_ERR_CHECK(  sg_wc_tx__diff__setup__header(pCtx, pvhItem, &pStringHeader)  );
		SG_ERR_CHECK(  SG_string__append__format(pCtx, pStringHeader, "=== %s\n", "Omitting details for lost item.")  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "header", SG_string__sz(pStringHeader))  );
	}
	else if (statusFlags & SG_WC_STATUS_FLAGS__U__FOUND)
	{
		// just build the header -- we never include content details for an uncontrolled item.
		SG_ERR_CHECK(  sg_wc_tx__diff__setup__header(pCtx, pvhItem, &pStringHeader)  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "header", SG_string__sz(pStringHeader))  );
	}
	else if ((statusFlags & SG_WC_STATUS_FLAGS__S__MERGE_CREATED)
			 && (statusFlags & SG_WC_STATUS_FLAGS__S__DELETED))
	{
		// just build the header -- no content to diff -- not present now and not present in baseline
		SG_ERR_CHECK(  sg_wc_tx__diff__setup__header(pCtx, pvhItem, &pStringHeader)  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "header", SG_string__sz(pStringHeader))  );
	}
	else if ((statusFlags & SG_WC_STATUS_FLAGS__S__UPDATE_CREATED)
			 && (statusFlags & SG_WC_STATUS_FLAGS__S__DELETED))
	{
		// just build the header -- no content to diff -- not present now and not present in baseline
		SG_ERR_CHECK(  sg_wc_tx__diff__setup__header(pCtx, pvhItem, &pStringHeader)  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "header", SG_string__sz(pStringHeader))  );
	}
	else if (statusFlags & (SG_WC_STATUS_FLAGS__S__ADDED
							|SG_WC_STATUS_FLAGS__S__MERGE_CREATED
							|SG_WC_STATUS_FLAGS__S__UPDATE_CREATED
							|SG_WC_STATUS_FLAGS__S__DELETED
							|SG_WC_STATUS_FLAGS__C__NON_DIR_MODIFIED))
	{
		// content added/deleted/changed.
		SG_ERR_CHECK(  sg_wc_tx__diff__setup__header(pCtx, pvhItem, &pStringHeader)  );
		SG_ERR_CHECK(  _diff__file__contents(pCtx, pData, pvhItem, statusFlags, pvhDiffItem, pStringHeader)  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "header", SG_string__sz(pStringHeader))  );
	}
	else
	{
		// just build the header -- content did not change -- must be a simple structural change.
		SG_ERR_CHECK(  sg_wc_tx__diff__setup__header(pCtx, pvhItem, &pStringHeader)  );
		SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhDiffItem, "header", SG_string__sz(pStringHeader))  );
	}

fail:
	SG_STRING_NULLFREE(pCtx, pStringHeader);
}
// TODO not sure we really want to pass this much stuff to this interface
void SG_vc_hooks__ASK__WIT__VALIDATE_ASSOCIATIONS(
    SG_context* pCtx, 
    SG_repo* pRepo, 
    const char* const* paszAssocs,
    SG_uint32 count_assocs,
	SG_varray *pBugs
    )
{
    SG_vhash* pvh_params = NULL;
    SG_vhash* pvh_result = NULL;
    char* psz_repo_id = NULL;
    char* psz_admin_id = NULL;
	SG_vhash* pvh_hook = NULL;
	const char* psz_js = NULL;
	SG_uint32 i = 0;
	SG_varray* pva_ids = NULL;
	const char* psz_descriptor_name = NULL;

    SG_ERR_CHECK(  sg_vc_hooks__lookup_by_interface__single_result(
                pCtx, 
                pRepo, 
                SG_VC_HOOK__INTERFACE__ASK__WIT__VALIDATE_ASSOCIATIONS,
                &pvh_hook
                )  );

    if (!pvh_hook)
		return;

	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_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, "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)  );
	}
	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])  );
	}

	SG_ERR_CHECK(  SG_vc_hooks__execute(pCtx, psz_js, pvh_params, &pvh_result)  );

	// TODO process the result
	if (pvh_result)
	{
		SG_bool hasErrors = SG_FALSE;
		SG_bool hasBugs = SG_FALSE;

		SG_ERR_CHECK(  SG_vhash__has(pCtx, pvh_result, "error", &hasErrors)  );

		if (hasErrors)
		{
			const char *emsg = NULL;

			SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvh_result, "error", &emsg)  );

			SG_ERR_THROW2( SG_ERR_VC_HOOK_REFUSED, (pCtx, "%s", emsg) );
		}

		SG_ERR_CHECK(  SG_vhash__has(pCtx, pvh_result, "bugs", &hasBugs)  );

		if (hasBugs && pBugs)
		{
			SG_varray *bugs = NULL;
			SG_ERR_CHECK(  SG_vhash__get__varray(pCtx, pvh_result, "bugs", &bugs)  );

			SG_ERR_CHECK(  SG_varray__copy_items(pCtx, bugs, pBugs)  );
		}
	}

fail:
    SG_VHASH_NULLFREE(pCtx, pvh_params);
    SG_VHASH_NULLFREE(pCtx, pvh_result);
    SG_VHASH_NULLFREE(pCtx, pvh_hook);
	SG_NULLFREE(pCtx, psz_repo_id);
	SG_NULLFREE(pCtx, psz_admin_id);
}
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);
}
/**
 * Deal with the status for 1 item and accumulate
 * the result in pvaStatus.
 *
 */
static void _sg_wc_tx__status(SG_context * pCtx,
							  SG_wc_tx * pWcTx,
							  SG_varray * pvaStatus,
							  const char * pszInput,
							  SG_uint32 depth,
							  SG_bool bListUnchanged,
							  SG_bool bNoIgnores,
							  SG_bool bNoTSC,
							  SG_bool bListSparse,
							  SG_bool bListReserved,
							  SG_vhash ** ppvhLegend)
{
	SG_string * pStringRepoPath = NULL;
	sg_wc_liveview_item * pLVI;			// we do not own this
	SG_bool bKnown;
	char chDomain;
	SG_vhash * pvhCSets = NULL;
	SG_vhash * pvhLegend = NULL;
	const char * pszWasLabel_l = "Baseline (B)";
	const char * pszWasLabel_r = "Working";

	SG_NULLARGCHECK_RETURN( pWcTx );
	SG_NULLARGCHECK_RETURN( pvaStatus );
	// pszInput is optional -- if omitted, we assume "@/".
	// ppvhLegend is optional

	SG_ERR_CHECK(  sg_wc_db__path__anything_to_repopath(pCtx, pWcTx->pDb, pszInput,
														SG_WC_DB__PATH__IMPORT_FLAGS__TREAT_NULL_AS_ROOT,
														&pStringRepoPath, &chDomain)  );

#if TRACE_WC_TX_STATUS
	SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR,
							   "SG_wc_tx__status: '%s' normalized to [domain %c] '%s'\n",
							   pszInput, chDomain, SG_string__sz(pStringRepoPath))  );
#endif

	SG_ERR_CHECK(  sg_wc_tx__liveview__fetch_item__domain(pCtx, pWcTx, pStringRepoPath,
														  &bKnown, &pLVI)  );
	if (!bKnown)
	{
		// We only get this if the path is completely bogus and
		// took us off into the weeds (as opposed to reporting
		// something just not-controlled).
		SG_ERR_THROW2(  SG_ERR_NOT_FOUND,
						(pCtx, "Unknown item '%s'.", SG_string__sz(pStringRepoPath))  );
	}

	SG_ERR_CHECK(  sg_wc_tx__rp__status__lvi(pCtx,
											 pWcTx,
											 pLVI,
											 depth,
											 bListUnchanged,
											 bNoIgnores,
											 bNoTSC,
											 bListSparse,
											 bListReserved,
											 pszWasLabel_l, pszWasLabel_r,
											 pvaStatus)  );

#if TRACE_WC_TX_STATUS
	SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR,
							   "SG_wc_tx__status: computed status [depth %d][bListUnchanged %d][bNoIgnores %d][bNoTSC %d][bListSparse %d][bListReserved %d] on '%s':\n",
							   depth,
							   bListUnchanged, bNoIgnores, bNoTSC, bListSparse, bListReserved,
							   SG_string__sz(pStringRepoPath))  );
	SG_ERR_IGNORE(  SG_varray_debug__dump_varray_of_vhashes_to_console(pCtx, pvaStatus, "")  );
#endif

	if (ppvhLegend)
	{
		const char * pszHid_x;

		SG_ERR_CHECK(  SG_wc_tx__get_wc_csets__vhash(pCtx, pWcTx, &pvhCSets)  );

		SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhLegend)  );
		SG_ERR_CHECK(  SG_vhash__check__sz(pCtx, pvhCSets, "A", &pszHid_x)  );
		if (pszHid_x)
			SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhLegend, "A", pszHid_x)  );

		SG_ERR_CHECK(  SG_vhash__check__sz(pCtx, pvhCSets, "L0", &pszHid_x)  );
		if (pszHid_x)
			SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhLegend, "B", pszHid_x)  );

		SG_ERR_CHECK(  SG_vhash__check__sz(pCtx, pvhCSets, "L1", &pszHid_x)  );
		if (pszHid_x)
			SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhLegend, "C", pszHid_x)  );

#if TRACE_WC_TX_STATUS
		SG_ERR_IGNORE(  SG_vhash_debug__dump_to_console__named(pCtx, pvhLegend, "Legend")  );
#endif

		*ppvhLegend = pvhLegend;
		pvhLegend = NULL;
	}

fail:
	SG_STRING_NULLFREE(pCtx, pStringRepoPath);
	SG_VHASH_NULLFREE(pCtx, pvhLegend);
	SG_VHASH_NULLFREE(pCtx, pvhCSets);
}
void u0026_jsonparser__create_2(SG_context* pCtx, SG_string* pStr)
{
	SG_jsonwriter* pjson = NULL;
	SG_vhash* pvh = NULL;
	SG_varray* pva = NULL;
	SG_uint32 i;
	char* pid = NULL;

	SG_ERR_CHECK(  SG_jsonwriter__alloc(pCtx, &pjson, pStr)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_start_object(pCtx, pjson)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__string__sz(pCtx, pjson, "hello", "world")  );

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__int64(pCtx, pjson, "x", 5)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__double(pCtx, pjson, "pi", 3.14159)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__bool(pCtx, pjson, "b1", SG_TRUE)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__bool(pCtx, pjson, "b2", SG_FALSE)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_begin_pair(pCtx, pjson, "furball")  );

	SG_ERR_CHECK(  SG_jsonwriter__write_start_array(pCtx, pjson)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_element__string__sz(pCtx, pjson, "plok")  );

	SG_ERR_CHECK(  SG_jsonwriter__write_element__double(pCtx, pjson, 47.567)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_element__int64(pCtx, pjson, 22222)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_element__null(pCtx, pjson)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_element__bool(pCtx, pjson, SG_TRUE)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_element__bool(pCtx, pjson, SG_FALSE)  );

	SG_ERR_CHECK(  SG_gid__alloc(pCtx, &pid)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_element__string__sz(pCtx, pjson, pid)  );
	SG_NULLFREE(pCtx, pid);

	SG_ERR_CHECK(  SG_jsonwriter__write_end_array(pCtx, pjson)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__null(pCtx, pjson, "nope")  );

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__string__sz(pCtx, pjson, "messy", U0026_MESSY)  );

	SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvh)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh, "fried", "tomatoes")  );
	SG_ERR_CHECK(  SG_vhash__add__int64(pCtx, pvh, "q", 333)  );

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__vhash(pCtx, pjson, "sub", pvh)  );

	SG_VHASH_NULLFREE(pCtx, pvh);

	SG_ERR_CHECK(  SG_VARRAY__ALLOC(pCtx, &pva)  );

	for (i=0; i<1000; i++)
	{
		SG_ERR_CHECK(  SG_varray__append__string__sz(pCtx, pva, "plok")  );

		SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pva, 22)  );

		SG_ERR_CHECK(  SG_varray__append__double(pCtx, pva, 1.414)  );

		SG_ERR_CHECK(  SG_varray__append__bool(pCtx, pva, SG_TRUE)  );

		SG_ERR_CHECK(  SG_varray__append__bool(pCtx, pva, SG_FALSE)  );

		SG_ERR_CHECK(  SG_varray__append__null(pCtx, pva)  );
	}

	SG_ERR_CHECK(  SG_jsonwriter__write_pair__varray(pCtx, pjson, "a", pva)  );

	SG_VARRAY_NULLFREE(pCtx, pva);

	SG_ERR_CHECK(  SG_jsonwriter__write_end_object(pCtx, pjson)  );

	SG_JSONWRITER_NULLFREE(pCtx, pjson);

	return;
fail:
	SG_VHASH_NULLFREE(pCtx, pvh);
	SG_VARRAY_NULLFREE(pCtx, pva);
	SG_JSONWRITER_NULLFREE(pCtx, pjson);
}
// TODO not sure we really want to pass this much stuff to this interface
void SG_vc_hooks__ASK__WIT__ADD_ASSOCIATIONS(
    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_vhash* pvh_hook = NULL;
    SG_vhash* pvh_params = NULL;
    SG_vhash* pvh_result = NULL;
    char* psz_repo_id = NULL;
    char* psz_admin_id = NULL;

    SG_ERR_CHECK(  sg_vc_hooks__lookup_by_interface__single_result(
                pCtx, 
                pRepo, 
                SG_VC_HOOK__INTERFACE__ASK__WIT__ADD_ASSOCIATIONS,
                &pvh_hook
                )  );

	if (pvh_hook)
    {
        const char* psz_js = NULL;
        SG_uint32 i = 0;
        SG_varray* pva_ids = NULL;
        const char* psz_descriptor_name = NULL;
        const char* psz_csid = NULL;
        SG_vhash* pvh_changeset = 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_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)  );

        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, &pvh_result)  );
        // TODO process the result

		if (pvh_result)
		{
			SG_bool hasErrors = SG_FALSE;

			SG_ERR_CHECK(  SG_vhash__has(pCtx, pvh_result, "error", &hasErrors)  );

			if (hasErrors)
			{
				const char *emsg = NULL;

				SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvh_result, "error", &emsg)  );

				SG_ERR_THROW2( SG_ERR_VC_HOOK_REFUSED, (pCtx, "\n:%s: %s", SG_VC_HOOK__INTERFACE__ASK__WIT__ADD_ASSOCIATIONS, emsg) );
			}
		}
    }
    
fail:
    SG_VHASH_NULLFREE(pCtx, pvh_params);
    SG_VHASH_NULLFREE(pCtx, pvh_result);
    SG_VHASH_NULLFREE(pCtx, pvh_hook);
	SG_NULLFREE(pCtx, psz_repo_id);
	SG_NULLFREE(pCtx, psz_admin_id);
}
void SG_sync_remote__push_clone__begin(
	SG_context* pCtx,
	const char* psz_repo_descriptor_name,
	const SG_vhash* pvhRepoInfo,
	const char** ppszCloneId)
{
	const char* psz_repo_id = NULL;
	const char* psz_admin_id = NULL;
	const char* psz_hash_method = NULL;
	const char* pszRefValidatedName = NULL;
	SG_vhash* pvhFullDescriptor = NULL; // pNewRepo owns this. Don't free.

	SG_vhash* pvhDescriptorPartial = NULL;
	SG_closet_descriptor_handle* ph = NULL;
	SG_repo* pNewRepo = NULL;
	const char* pszRefCloneId = NULL;

	SG_NULLARGCHECK_RETURN(ppszCloneId);

	SG_ERR_CHECK_RETURN(  _remote_clone_allowed(pCtx)  );

	/* We'll create a descriptor and empty repo immediately, so that we validate and claim the name
	 * before a potentially long upload. */

	SG_ERR_CHECK_RETURN(  SG_vhash__get__sz(pCtx, pvhRepoInfo, SG_SYNC_REPO_INFO_KEY__REPO_ID, &psz_repo_id)  );
	SG_ERR_CHECK_RETURN(  SG_vhash__get__sz(pCtx, pvhRepoInfo, SG_SYNC_REPO_INFO_KEY__ADMIN_ID, &psz_admin_id)  );
	SG_ERR_CHECK_RETURN(  SG_vhash__get__sz(pCtx, pvhRepoInfo, SG_SYNC_REPO_INFO_KEY__HASH_METHOD, &psz_hash_method)  );

	SG_ERR_CHECK(  SG_closet__descriptors__add_begin(pCtx, 
		psz_repo_descriptor_name, 
		NULL, 
		psz_repo_id, 
		psz_admin_id, 
		&pszRefValidatedName, &pvhDescriptorPartial, &ph)  );

	SG_ERR_CHECK(  SG_repo__create_repo_instance(pCtx,
		pszRefValidatedName,
		pvhDescriptorPartial,
		SG_TRUE,
		psz_hash_method,
		psz_repo_id,
		psz_admin_id,
		&pNewRepo)  );
	SG_VHASH_NULLFREE(pCtx, pvhDescriptorPartial);

	SG_ERR_CHECK_RETURN(  SG_staging__clone__create(pCtx, psz_repo_descriptor_name, pvhRepoInfo, &pszRefCloneId)  );

	/* We temporarily add the clone ID to the repo descriptor so that we can verify 
	 * we're committing to the correct repo later, when the fragball's been uploaded. */
	SG_ERR_CHECK(  SG_repo__get_descriptor__ref(pCtx, pNewRepo, &pvhFullDescriptor)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhFullDescriptor, SG_SYNC_DESCRIPTOR_KEY__CLONE_ID, pszRefCloneId)  );

	SG_ERR_CHECK(  SG_closet__descriptors__add_commit(pCtx, &ph, pvhFullDescriptor, SG_REPO_STATUS__CLONING)  );

	SG_REPO_NULLFREE(pCtx, pNewRepo);

	*ppszCloneId = pszRefCloneId;

	return;

fail:
	SG_VHASH_NULLFREE(pCtx, pvhDescriptorPartial);
	SG_REPO_NULLFREE(pCtx, pNewRepo);
	SG_ERR_IGNORE(  SG_closet__descriptors__add_abort(pCtx, &ph)  );
}
static void _tree__add_next_node_to_results(SG_context * pCtx, _tree_t * pTree, SG_varray * pResults, SG_uint32 * pCountResults, SG_bool * pbLastResultWasIndented)
{
	SG_vhash * pResult = NULL;
	SG_varray * pChildren = NULL;
	SG_uint32 i;
	SG_varray * pTmp = NULL;

	// Add pTree->pNextResult to results list.
	SG_ERR_CHECK(  SG_varray__appendnew__vhash(pCtx, pResults, &pResult)  );
	SG_ERR_CHECK(  SG_vhash__add__int64(pCtx, pResult, "revno", pTree->pNextResult->revno)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pResult, "changeset_id", pTree->pNextResult->pszHidRef)  );
	SG_ERR_CHECK(  SG_vhash__add__vhash(pCtx, pResult, "parents", &pTree->pNextResult->pVcParents)  );
	SG_ERR_CHECK(  SG_vhash__addnew__varray(pCtx, pResult, "displayChildren", &pChildren)  );
	for(i=0; i<pTree->pNextResult->displayChildren.count; ++i)
		SG_ERR_CHECK(  SG_varray__append__int64(pCtx, pChildren, pTree->pNextResult->displayChildren.p[i]->revno)  );
	if(pTree->pNextResult->displayChildren.count>1 && pTree->indentLevel>0)
	{
		SG_varray * pContinuationToken = NULL;
		SG_ERR_CHECK(  SG_vhash__addnew__varray(pCtx, pResult, "continuationToken", &pContinuationToken)  );
		SG_ERR_CHECK(  _tree__generate_continuation_token(pCtx, pTree, pContinuationToken)  );
	}
	SG_ERR_CHECK(  SG_vhash__add__int64(pCtx, pResult, "indent", pTree->indentLevel)  );
	*pbLastResultWasIndented = (pTree->indentLevel > 0);
	if(pTree->pNextResult->pDisplayParent!=NULL)
	{
		SG_ERR_CHECK(  SG_vhash__add__int64(pCtx, pResult, "displayParent", pTree->pNextResult->pDisplayParent->revno)  );
		SG_ERR_CHECK(  SG_vhash__add__int64(pCtx, pResult, "indexInParent", pTree->pNextResult->pDisplayParent->displayChildren.count)  );
		SG_ASSERT(*pbLastResultWasIndented);
	}
	else
	{
		SG_ASSERT(!*pbLastResultWasIndented);
	}
	++(*pCountResults);

	// Advance pTree->pNextResult pointer to next result.
	while(pTree->pNextResult->displayChildren.count==0 && pTree->pNextResult!=pTree->pRoot)
	{
		// We have already added this node and all children to the results.
		// Free the memory that will not be reused, and then put the node
		// on the "free list".
		_node__free_nonreusable_memory(pCtx, pTree->pNextResult);
		pTree->pNextResult->displayChildren.p[0] = pTree->pFreeList;
		pTree->pNextResult->displayChildren.count = 1;
		pTree->pFreeList = pTree->pNextResult;

		// Move back up up in the tree.
		pTree->pNextResult = pTree->pNextResult->pDisplayParent;

		if(pTree->pNextResult!=NULL)
		{
			// The node we just freed... Remove it from its display parent too.
			--pTree->pNextResult->displayChildren.count;

			// All children but the leftmost one are indented by 1 from the parent.
			if(pTree->pNextResult->displayChildren.count>0)
				--pTree->indentLevel;
		}
	}
	if(pTree->pNextResult->displayChildren.count>0)
	{
		SG_uint32 i = pTree->pNextResult->displayChildren.count-1;
		pTree->pNextResult = pTree->pNextResult->displayChildren.p[i];
		if(i>=1)
			++pTree->indentLevel;
	}
	else
	{
		pTree->pNextResult = NULL;
	}

	// If we advanced past root...
	if(pTree->pNextResult==NULL || pTree->pNextResult==pTree->pRoot->displayChildren.p[0])
	{
		// Out with the old root, in with the new...

		_node__free_nonreusable_memory(pCtx, pTree->pRoot);
		pTree->pRoot->displayChildren.p[0] = pTree->pFreeList;
		pTree->pRoot->displayChildren.count = 1;
		pTree->pFreeList = pTree->pRoot;

		pTree->pRoot = pTree->pNextResult;
		if(pTree->pRoot!=NULL)
			pTree->pRoot->pDisplayParent = NULL;
	}

	return;
fail:
	SG_VARRAY_NULLFREE(pCtx, pTmp);
}
void SG_sync_remote__get_repo_info(
	SG_context* pCtx,
	SG_repo* pRepo,
	SG_bool bIncludeBranches,
    SG_bool b_include_areas,
    SG_vhash** ppvh)
{
    SG_vhash* pvh = NULL;
	char* pszRepoId = NULL;
	char* pszAdminId = NULL;
	char* pszHashMethod = NULL;
	SG_uint32  count_dagnums = 0;
	SG_uint64* paDagNums     = NULL;
    SG_uint32 i = 0;
    SG_vhash* pvh_dags = NULL;
    SG_vhash* pvh_areas = NULL;
	SG_vhash* pvhBranchPile = NULL;

	SG_bool bHasBranchDag = SG_FALSE;

	SG_NULLARGCHECK_RETURN(pRepo);

    SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvh)  );

	/* Protocol version */
	SG_ERR_CHECK(  SG_vhash__add__int64(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__PROTOCOL_VERSION, 1)  );


	/* Basic repository info */
	SG_ERR_CHECK(  SG_repo__get_repo_id(pCtx, pRepo, &pszRepoId)  );
    SG_ERR_CHECK(  SG_repo__get_admin_id(pCtx, pRepo, &pszAdminId)  );
    SG_ERR_CHECK(  SG_repo__get_hash_method(pCtx, pRepo, &pszHashMethod)  );
    SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__REPO_ID, pszRepoId)  );
    SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__ADMIN_ID, pszAdminId)  );
    SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvh, SG_SYNC_REPO_INFO_KEY__HASH_METHOD, pszHashMethod)  );

	/* All DAGs in the repository */
	SG_ERR_CHECK(  SG_repo__list_dags(pCtx, pRepo, &count_dagnums, &paDagNums)  );
    SG_ERR_CHECK(  SG_vhash__addnew__vhash(pCtx, pvh, "dags", &pvh_dags)  );
    for (i=0; i<count_dagnums; i++)
    {
        char buf_dagnum[SG_DAGNUM__BUF_MAX__HEX];

        SG_ERR_CHECK_RETURN(  SG_dagnum__to_sz__hex(pCtx, paDagNums[i], buf_dagnum, sizeof(buf_dagnum))  );
        SG_ERR_CHECK(  SG_vhash__add__null(pCtx, pvh_dags, buf_dagnum)  );

		/* Asking for a DAG for the first time in a repo will create that DAG.
		 * When pushing into an empty repo, we don't want this initial query to create 
		 * empty new DAGs, so we make sure they exist before we query them. */
		if (paDagNums[i] == SG_DAGNUM__VC_BRANCHES)
			bHasBranchDag = SG_TRUE;
    }
    
    // TODO the following code is a problem, because it requires that this repo
    // instance have indexes, and we would prefer to preserve the ability of
    // an index-free instance to support push, pull, and clone.

	/* All areas in the repository */
    if (b_include_areas)
    {
        SG_ERR_CHECK(  SG_area__list(pCtx, pRepo, &pvh_areas)  );
        if (pvh_areas)
        {
            SG_ERR_CHECK(  SG_vhash__add__vhash(pCtx, pvh, "areas", &pvh_areas)  );
        }
    }

	/* Branches */
	if (bIncludeBranches && bHasBranchDag)
	{
		SG_ERR_CHECK(  SG_vc_branches__cleanup(pCtx, pRepo, &pvhBranchPile)  );
		if (pvhBranchPile)
		{
			SG_bool bHasBranches;

			SG_ERR_CHECK(  SG_vhash__has(pCtx, pvhBranchPile, "branches", &bHasBranches)  );
			if (bHasBranches)
				SG_ERR_CHECK(  SG_vhash__add__vhash(pCtx, pvh, "branches", &pvhBranchPile)  );
		}
	}

    *ppvh = pvh;
    pvh = NULL;

	/* fall through */
fail:
	SG_NULLFREE(pCtx, paDagNums);
    SG_VHASH_NULLFREE(pCtx, pvh);
	SG_NULLFREE(pCtx, pszRepoId);
	SG_NULLFREE(pCtx, pszAdminId);
	SG_NULLFREE(pCtx, pszHashMethod);
	SG_VHASH_NULLFREE(pCtx, pvh_areas);
	SG_VHASH_NULLFREE(pCtx, pvhBranchPile);
}