Example #1
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);
}
Example #2
0
static void _getDescriptorName(
	SG_context *pCtx,
	const _request_headers *pRequestHeaders,
	SG_string **ppRepoName)
{
	SG_pathname *pPathCwd = NULL;

	if (_startsWith("repos/", pRequestHeaders->pUri) && (strlen(pRequestHeaders->pUri) > 6))
	{
		const char *nameStart = pRequestHeaders->pUri + 6;
		const char *nameEnd = strchr(nameStart, '/');

		SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, ppRepoName)  );

		if (nameEnd == NULL)
			SG_ERR_CHECK(  SG_string__set__sz(pCtx, *ppRepoName, nameStart)  );
		else
			SG_ERR_CHECK(  SG_string__set__buf_len(pCtx, *ppRepoName, (const SG_byte *)nameStart,
												   (nameEnd - nameStart) * sizeof(char))  );
	}
	else
	{
		SG_ERR_CHECK(  SG_PATHNAME__ALLOC(pCtx, &pPathCwd)  );
		SG_ERR_CHECK(  SG_pathname__set__from_cwd(pCtx, pPathCwd)  );
		SG_ERR_CHECK(  SG_workingdir__find_mapping(pCtx, pPathCwd, NULL, ppRepoName, NULL)  );
	}

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPathCwd);
}
/**
 * "Throws" SG_ERR_NOT_A_WORKING_COPY if the cwd's not inside a working copy.
 */
void SG_cmd_util__get_descriptor_name_from_cwd(SG_context* pCtx, SG_string** ppstrDescriptorName, SG_pathname** ppPathCwd)
{
	SG_pathname* pPathCwd = NULL;
	SG_string* pstrRepoDescriptorName = NULL;

	SG_ERR_CHECK(  SG_PATHNAME__ALLOC(pCtx, &pPathCwd)  );
	SG_ERR_CHECK(  SG_pathname__set__from_cwd(pCtx, pPathCwd)  );

	SG_ERR_CHECK(  SG_workingdir__find_mapping(pCtx, pPathCwd, NULL, &pstrRepoDescriptorName, NULL)  );

	SG_RETURN_AND_NULL(pstrRepoDescriptorName, ppstrDescriptorName);
	SG_RETURN_AND_NULL(pPathCwd, ppPathCwd);

	/* fall through */
fail:
	SG_PATHNAME_NULLFREE(pCtx, pPathCwd);
	SG_STRING_NULLFREE(pCtx, pstrRepoDescriptorName);
}
Example #4
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);
}
Example #6
0
/**
 * Handle the UPDATE command.
 *
 *
 */
void do_cmd_update(SG_context * pCtx,
				   SG_option_state * pOptSt)
{
	SG_pathname * pPathCwd = NULL;
	SG_pendingtree * pPendingTree = NULL;
	SG_repo * pRepo;
	char * pszTargetChangeset = NULL;
	char * pszBaselineBeforeUpdate = NULL;

	// use the current directory to find the pending-tree, the repo, and the current baseline.

	SG_ERR_CHECK(  SG_PATHNAME__ALLOC(pCtx, &pPathCwd)  );
	SG_ERR_CHECK(  SG_pathname__set__from_cwd(pCtx, pPathCwd)  );

	SG_ERR_CHECK(  SG_PENDINGTREE__ALLOC(pCtx, pPathCwd, pOptSt->bIgnoreWarnings, &pPendingTree)  );
	SG_ERR_CHECK(  SG_pendingtree__get_repo(pCtx, pPendingTree, &pRepo)  );

	SG_ERR_CHECK(  _get_baseline(pCtx, pPendingTree, &pszBaselineBeforeUpdate)  );

	// determine the target changeset

	// we check that we have at most 1 rev *or* 1 tag up in sg.c

	if (pOptSt->iCountRevs == 1)
	{
		SG_rev_tag_obj* pRTobj = NULL;
		const char * psz_rev_0;

		SG_ERR_CHECK(  SG_vector__get(pCtx, pOptSt->pvec_rev_tags, 0, (void**)&pRTobj)  );

		psz_rev_0 = pRTobj->pszRevTag;

		SG_ERR_CHECK(  SG_repo__hidlookup__dagnode(pCtx, pRepo, SG_DAGNUM__VERSION_CONTROL, psz_rev_0, &pszTargetChangeset)  );
	}
	else if (pOptSt->iCountTags == 1)
	{
		SG_rev_tag_obj* pRTobj = NULL;
		const char * psz_tag_0;

		SG_ERR_CHECK(  SG_vector__get(pCtx, pOptSt->pvec_rev_tags, 0, (void**)&pRTobj)  );

		psz_tag_0 = pRTobj->pszRevTag;

		SG_ERR_CHECK(  SG_vc_tags__lookup__tag(pCtx, pRepo, psz_tag_0, &pszTargetChangeset)  );
	}
	else
	{
		// pass NULL for target changeset and let the UPDATE code find the proper head/tip.
	}

	SG_ERR_CHECK(  _my_do_cmd_update(pCtx, pOptSt, pPendingTree, pszTargetChangeset)  );
	SG_PENDINGTREE_NULLFREE(pCtx, pPendingTree);
	pRepo = NULL;

	if (pszTargetChangeset == NULL)
	{
		// if they didn't ask for a specific changeset (and we successfully
		// went to the SINGLE/UNIQUE DESCENDANT HEAD from their (then current)
		// BASELINE, we should look around and see if there are other heads/leaves
		// and advise them to MERGE with them.
		//
		// Since we did successfully do the UPDATE we should exit with OK, so
		// I'm going to do all of this advisory stuff in an IGNORE.

		SG_ERR_IGNORE(  _advise_after_update(pCtx, pOptSt, pPathCwd, pszBaselineBeforeUpdate)  );
	}

fail:
	SG_PENDINGTREE_NULLFREE(pCtx, pPendingTree);
	SG_PATHNAME_NULLFREE(pCtx, pPathCwd);
	SG_NULLFREE(pCtx, pszTargetChangeset);
	SG_NULLFREE(pCtx, pszBaselineBeforeUpdate);
}
Example #7
0
/**
 * Handle the RESOLVE command.
 *
 *
 */
void do_cmd_resolve(SG_context * pCtx,
					SG_option_state * pOptSt,
					SG_uint32 count_args, const char** paszArgs)
{
	struct _resolve_data data;
	SG_uint32 sum = 0;
	SG_bool bAll = SG_FALSE;
	SG_bool bWantResolved = SG_FALSE;
	SG_bool bWantUnresolved = SG_FALSE;
	SG_bool bReqArg = SG_FALSE;

	memset(&data, 0, sizeof(data));
	data.pPathCwd = NULL;
	data.pPendingTree = NULL;
	data.psaGids = NULL;
	data.bIgnoreWarnings = SG_TRUE;			// TODO what should this be?

	// allow at most ONE of the command options.
	//
	// the --{List,Mark,Unmark}All options do not allow ARGs.
	// 
	// the --{Mark,Unmark} require at least one ARG.
	// the --List allows 0 or more ARGs.
	//
	// if no command option, allow 0 or more ARGs.
	//
	// most commands do not require there to be issues; rather
	// they just don't do anything.
	//
	// WARNING: We set sg_cl_options[].has_arg to 0 for all of
	//          our commands options so that we get all of the
	//          pathnames in ARGs rather than bound to the option.
	//          That is, I want to be able to say:
	//               vv resolve --mark foo bar
	//          rather than:
	//               vv resolve --mark foo --mark bar
	//
	//          It also allows me to have:
	//               vv resolve --list
	//          and
	//               vv resolve --list foo

	if (pOptSt->bListAll)		{ sum++; bAll = SG_TRUE;  bWantResolved = SG_TRUE;  bWantUnresolved = SG_TRUE;  bReqArg = SG_FALSE; }
	if (pOptSt->bMarkAll)		{ sum++; bAll = SG_TRUE;  bWantResolved = SG_TRUE;  bWantUnresolved = SG_TRUE;  bReqArg = SG_FALSE; }
//	if (pOptSt->bUnmarkAll)		{ sum++; bAll = SG_TRUE;  bWantResolved = SG_TRUE;  bWantUnresolved = SG_TRUE;  bReqArg = SG_FALSE; }
	if (pOptSt->bList)
	{
		if (count_args == 0)	{ sum++; bAll = SG_FALSE; bWantResolved = SG_FALSE; bWantUnresolved = SG_TRUE;  bReqArg = SG_FALSE; }
		else					{ sum++; bAll = SG_FALSE; bWantResolved = SG_TRUE;  bWantUnresolved = SG_TRUE;  bReqArg = SG_FALSE; }
	}
	if (pOptSt->bMark)			{ sum++; bAll = SG_FALSE; bWantResolved = SG_FALSE; bWantUnresolved = SG_TRUE;  bReqArg = SG_TRUE;  }
//	if (pOptSt->bUnmark)		{ sum++; bAll = SG_FALSE; bWantResolved = SG_TRUE;  bWantUnresolved = SG_FALSE; bReqArg = SG_TRUE;  }
	if (sum == 0)				{        bAll = SG_FALSE; bWantResolved = SG_FALSE; bWantUnresolved = SG_TRUE;  bReqArg = SG_FALSE; }

	if (sum > 1)
		SG_ERR_THROW(  SG_ERR_USAGE  );

	if (bReqArg && (count_args == 0))
		SG_ERR_THROW(  SG_ERR_USAGE  );

	if (bAll && (count_args > 0))
		SG_ERR_THROW(  SG_ERR_USAGE  );

	SG_ERR_CHECK(  SG_PATHNAME__ALLOC(pCtx, &data.pPathCwd)  );
	SG_ERR_CHECK(  SG_pathname__set__from_cwd(pCtx, data.pPathCwd)  );

	// Do a complete scan first.  This ensures that the pendingtree knows
	// about everything that is dirty in the WD and helps ensure that every
	// issue in the issues list has a ptnode in the pendingtree.
	// 
	// TODO 2010/07/16 Technically, this should NOT be required.  But it
	// TODO            helps.  The problem is that when a file is edited
	// TODO            we don't automatically get the notification, rather
	// TODO            we do a status aka scan (and/or use the timestamp
	// TODO            cache) when various commands start which detect
	// TODO            file content changes.  So the fact that the MERGE
	// TODO            may have written a bunch of merged/edited files
	// TODO            doesn't necessarily mean that they are listed in
	// TODO            the pendingtree -- because the user may have edited
	// TODO            them again (or edited other files) since the merge
	// TODO            completed.  So we scan.
	// TODO
	// TODO            See also the comment in sg.c:do_cmd_commit() for sprawl-809.
	// TODO
	// TODO            What this scan is helping to hide is a problem where
	// TODO            we're hitting the issues list for GIDs and then
	// TODO            using SG_pendingtree__find_repo_path_by_gid() to
	// TODO            dynamically convert it into a "live/current" repo-path.
	// TODO            and it assumes that it is only called for dirty entries
	// TODO            (or rather, for entries that have a ptnode).  We need
	// TODO            to fix that.

	SG_ERR_CHECK(  SG_PENDINGTREE__ALLOC(pCtx, data.pPathCwd, data.bIgnoreWarnings, &data.pPendingTree)  );
	SG_ERR_CHECK(  SG_pendingtree__scan(pCtx, data.pPendingTree, SG_TRUE, NULL, 0, NULL, 0)  );
	SG_PENDINGTREE_NULLFREE(pCtx, data.pPendingTree);

	// Now load the pendingtree for real.

	SG_ERR_CHECK(  SG_PENDINGTREE__ALLOC(pCtx, data.pPathCwd, data.bIgnoreWarnings, &data.pPendingTree)  );

	if (count_args > 0)
		SG_ERR_CHECK(  _resolve__map_args_to_gids(pCtx, &data, count_args, paszArgs, bWantResolved, bWantUnresolved)  );
	else
		SG_ERR_CHECK(  _resolve__get_all_issue_gids(pCtx, &data, bWantResolved, bWantUnresolved)  );

	//////////////////////////////////////////////////////////////////

	if (pOptSt->bListAll || pOptSt->bList)
	{
		SG_ERR_CHECK(  _resolve__do_list(pCtx, &data)  );
	}
	else if (pOptSt->bMarkAll || pOptSt->bMark)
	{
		SG_ERR_CHECK(  _resolve__do_mark(pCtx, &data, SG_TRUE)  );
	}
//	else if (pOptSt->bUnmarkAll || pOptSt->bUnmark)
//	{
//		SG_ERR_CHECK(  _resolve__do_mark(pCtx, &data, SG_FALSE)  );
//	}
	else // no command option given -- assume we want to FIX the issues
	{
		SG_ERR_CHECK(  _resolve__do_fix(pCtx, &data)  );
	}

fail:
	SG_PATHNAME_NULLFREE(pCtx, data.pPathCwd);
	SG_PENDINGTREE_NULLFREE(pCtx, data.pPendingTree);
	SG_STRINGARRAY_NULLFREE(pCtx, data.psaGids);
}
/**
 * Import a raw string (probably as the user typed it) and
 * transform it into a repo-path.  And scrub it as appropriate.
 *
 * This routine is inside WC and takes a "pDb" because we need
 * to be able to interpret a null or relative path and convert
 * them into a wd-root-based current/live repo-path.
 *
 * But we also need to be able to call it for arbitrary-cset-based
 * repo-paths that when we don't have a WD.
 *
 * We return both the observed/computed repo-path and the
 * observed/computed repo-path-domain.  The returned repo-path
 * will contain the repo-path-domain so that they are always
 * in normal form.
 *
 * It is up to the caller to decide whether the domain is
 * valid for their purposes and/or throw.
 *
 */
void sg_wc_db__path__anything_to_repopath(SG_context * pCtx,
										  const sg_wc_db * pDb,
										  const char * pszInput,
										  sg_wc_db__path__import_flags flags,
										  SG_string ** ppStringRepoPath,
										  char * pcDomain)
{
	SG_pathname * pPathTemp = NULL;
	SG_string * pStringTemp = NULL;
	SG_bool bValid;

	if (!pszInput || !*pszInput)	// when null, assume either CWD or ROOT.
	{
		switch (flags)
		{
		default:
		case SG_WC_DB__PATH__IMPORT_FLAGS__TREAT_NULL_AS_ERROR:
			SG_ERR_THROW_RETURN(  SG_ERR_INVALIDARG  );

		case SG_WC_DB__PATH__IMPORT_FLAGS__TREAT_NULL_AS_ROOT:
			SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pPathTemp, pDb->pPathWorkingDirectoryTop)  );
			SG_ERR_CHECK(  sg_wc_db__path__absolute_to_repopath(pCtx, pDb, pPathTemp, ppStringRepoPath)  );
			SG_PATHNAME_NULLFREE(pCtx, pPathTemp);
			*pcDomain = SG_WC__REPO_PATH_DOMAIN__LIVE;
			return;

		case SG_WC_DB__PATH__IMPORT_FLAGS__TREAT_NULL_AS_CWD:
			SG_ERR_CHECK(  SG_PATHNAME__ALLOC(pCtx, &pPathTemp)  );
			SG_ERR_CHECK(  SG_pathname__set__from_cwd(pCtx, pPathTemp)  );
			SG_ERR_CHECK(  sg_wc_db__path__absolute_to_repopath(pCtx, pDb, pPathTemp, ppStringRepoPath)  );
			SG_PATHNAME_NULLFREE(pCtx, pPathTemp);
			*pcDomain = SG_WC__REPO_PATH_DOMAIN__LIVE;
			return;
		}
	}

	if (pszInput[0] != '@')			// a non-repo pathname
	{
		SG_bool bPathIsRelative = SG_FALSE;
		SG_ERR_CHECK(  SG_pathname__is_relative(pCtx, pszInput, &bPathIsRelative)  );
		if (bPathIsRelative)
			SG_ERR_CHECK(  _check_if_relative_paths_allowed(pCtx, pDb)  );

		SG_ERR_CHECK(  SG_PATHNAME__ALLOC__SZ(pCtx, &pPathTemp, pszInput)  );
		SG_ERR_CHECK(  sg_wc_db__path__absolute_to_repopath(pCtx, pDb, pPathTemp, ppStringRepoPath)  );
		SG_PATHNAME_NULLFREE(pCtx, pPathTemp);
		*pcDomain = SG_WC__REPO_PATH_DOMAIN__LIVE;
		return;
	}

	// if they gave us something that started with a "@" we
	// either return it as is or throw if we can't parse it.
	// this ensures that any error messages don't leave the
	// domain out of the repo-path.

	switch (pszInput[1])
	{
	case 0:
		SG_ERR_THROW2_RETURN(  SG_ERR_INVALIDARG,
							   (pCtx, "The input '%s' is not a valid repo-path input.", pszInput)  );

	case SG_WC__REPO_PATH_DOMAIN__G:		// domain "@g..." is a GID and not a path.
		// A '@g' domain input should look like
		// "@gae9ce8e7eaf04b58aef9470b439329f32249aae2358511e1b77b002500da2b78"
		// That is, the 'g' domain specifier is shared with the 'g' in the
		// full GID.  For now a full GID is required; we don't try to do the
		// unique prefix trick like we do for CSET HIDs in a REV-SPEC.  We
		// may think about adding that later.

		SG_ERR_CHECK(  SG_gid__verify_format(pCtx, &pszInput[1], &bValid)  );
		if (!bValid)
			SG_ERR_THROW2(  SG_ERR_INVALIDARG,
							(pCtx, "The input '%s' is not a valid gid-domain input.", pszInput)  );
		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, ppStringRepoPath, pszInput)  );
		*pcDomain = SG_WC__REPO_PATH_DOMAIN__G;
		return;

	case SG_WC__REPO_PATH_DOMAIN__T:		// domain "@t..." is a temporary GID and not a path.
		// A '@t' domain input should look like
		// "@tae9ce8e7eaf04b58aef9470b439329f32249aae2358511e1b77b002500da2b78"
		// That is, the 't' domain specifier replaces the normal 'g' in the
		// full GID.  For now a full GID is required; we don't try to do the
		// unique prefix trick like we do for CSET HIDs in a REV-SPEC.  We
		// may think about adding that later.  We use a 't' when reporting
		// STATUS rather than a 'g' as a reminder that the ID is only valid
		// until the end of the transaction.
		//
		// make a g-version of the string so we can validate it.
		// but still return the t-version.

		SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &pStringTemp)  );
		SG_ERR_CHECK(  SG_string__sprintf(pCtx, pStringTemp, "%c%s",
										  SG_WC__REPO_PATH_DOMAIN__G,
										  &pszInput[2])  );
		SG_ERR_CHECK(  SG_gid__verify_format(pCtx, SG_string__sz(pStringTemp), &bValid)  );
		SG_STRING_NULLFREE(pCtx, pStringTemp);
		if (!bValid)
			SG_ERR_THROW2(  SG_ERR_INVALIDARG,
							(pCtx, "The input '%s' is not a valid temp-gid-domain input.", pszInput)  );
		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, ppStringRepoPath, pszInput)  );
		*pcDomain = SG_WC__REPO_PATH_DOMAIN__T;
		return;

	case SG_WC__REPO_PATH_DOMAIN__LIVE:		// domain "@/foo..." is a normal current/live repo-path
		SG_ERR_CHECK(  _complain_if_unnormalized(pCtx, &pszInput[1])  );
		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, ppStringRepoPath, pszInput)  );
		*pcDomain = SG_WC__REPO_PATH_DOMAIN__LIVE;
		return;

	default:		// we allow any other SINGLE character to be a domain.
		if (pszInput[2] != '/')
			SG_ERR_THROW2_RETURN(  SG_ERR_INVALIDARG,
								   (pCtx, "The input '%s' is not a valid repo-path input.", pszInput)  );

		// we have "@x/foo...." for some character x.
		SG_ERR_CHECK(  _complain_if_unnormalized(pCtx, &pszInput[2])  );
		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, ppStringRepoPath, pszInput)  );
		*pcDomain = pszInput[1];
		return;
	}

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPathTemp);
	SG_STRING_NULLFREE(pCtx, pStringTemp);
}