Jump to content


P4Api client.GetFileMappings not returns leading minus/dash sign for branched files

where p4api.net branch

  • Please log in to reply
4 replies to this topic

#1 Lubos Suk

Lubos Suk

    Newbie

  • Members
  • Pip
  • 5 posts

Posted 19 December 2019 - 06:38 AM

I have files on P4 server under multiple branches e.g.

stream branch1 is base
//depot/branch1/files/file.txt

stream branch2 inherits from branch1
import...
share files/...

stream branch3 inherits from branch2
import...
share files/...

and then workspace called testWorkspace which is using stream3

When i use commandline

p4 -c testWorkspace where somepath\file.txt

i get following result

-//branch1/files/file.txt {client path depot path}
-//branch2/files/file.txt {client path depot path}
//branch3/files/file.txt {client path depot path}

and from that i can tell that file.txt in client testWorkspace should be accessed via branch3 (so from this depot path i will get FileSpec, Metadata, edit, etc

But when i try to do same via P4api.net and use

Client.GetClientFileMappings("somepath\file.txt")
or

P4Command cmd3 = new P4Command(con, "where", true, "somepath\file.txt");
P4CommandResult result3 = cmd3.Run();

i got similar result but without leading minus (dash -) signs

//branch1/files/file.txt {client path depot path}
//branch2/files/file.txt {client path depot path}
//branch3/files/file.txt {client path depot path}

And i dont know what am i doing wrong here.

What i need is to get information to which branch current file for given workspace belongs, or even better get its correct FileSpec so i can use MoveFile, Add and so on. But i only get paths to all branches and can recognize to which branch it belongs for current workspace

#2 Lubos Suk

Lubos Suk

    Newbie

  • Members
  • Pip
  • 5 posts

Posted 07 January 2020 - 06:56 AM

So i discused this with P4 team member and they confirmed that GetClientFileMappings realy not returns information about exclusion.
They offered me a "workaround"
P4Command cmd3 = new P4Command(con, "where", true, "somepath\file.txt");
P4CommandResult result3 = cmd3.Run();
if (result3.TaggedOutput!=null)
{
	 List<string> depotFiles = new List<string>();
	 foreach(TaggedObject taggedObject in results3.TaggedOutput)
	 {
		 if (taggedObject.ContainsKey("unmap"))
		 {
			 continue;
		 }
		 else
		 {
			 string path = "";
			 taggedObject.TryGetValue("depotFile", out path);
			 depotFiles.Add(path);
		 }
	 }
}


which is working for me. In original question, i mentions this not returns leading '-' which is true. But taggedObject instead contains key "unmap" which is enough to determine information.
I didnt notice this for first time, because i passed argument in wrong way. "file1.txt file2.txt" as simple string, not array of strings.
I also figured out one more "workaround" which is much more uglier (use p4 commandline, process.Start() and parse string result)
string commandText = $"/C p4 -u {UserName} -c {Client.Name} where {string.Join(" ", filePaths)}";[/size][/font][/color]
var process = new Process()
{
StartInfo = new ProcessStartInfo()
{
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "cmd.exe",
Arguments = commandText,
RedirectStandardError = true,
RedirectStandardOutput = true[color=#242729][font=Arial, "Helvetica Neue", Helvetica, sans-serif][size=4]
}
};[/size][/font][/color]
process.Start();[color=#242729][font=Arial, "Helvetica Neue", Helvetica, sans-serif][size=4]
string processTextResult = process.StandardOutput.ReadToEnd();[/size][/font][/color]
var exitCode = process.ExitCode;
var errorMsg = process.StandardError.ReadToEnd();[color=#242729][font=Arial, "Helvetica Neue", Helvetica, sans-serif][size=4]

process.Dispose();
if (exitCode == 0)
{
var resultLines = processTextResult.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);[/size][/font][/color]
List<FileSpec> fileSpecResults = new List<FileSpec>();[color=#242729][font=Arial, "Helvetica Neue", Helvetica, sans-serif][size=4]
foreach (var resultLine in resultLines)
{
var splitedLine = resultLine.Split(' ');[/size][/font][/color]
if (!splitedLine.First().StartsWith("-"))
{
fileSpecResults.Add(new FileSpec(new DepotPath(splitedLine[0]), new ClientPath(splitedLine[1]), new LocalPath(splitedLine[2]), null));
}
}[color=#242729][font=Arial, "Helvetica Neue", Helvetica, sans-serif][size=4]
return fileSpecResults;
}
else
{
Logger.TraceError("P4 error - get file spec :" + errorMsg);
return new List<FileSpec>();
}


#3 Sambwise

Sambwise

    Advanced Member

  • Members
  • PipPipPip
  • 1175 posts

Posted 07 January 2020 - 03:44 PM

If your intent is to figure out which depot path a given client path maps to, why not just pass the client path directly to GetFileMetadata or whatever method you're ultimately trying to pass the depot path to?  Almost every Perforce command that operates on depot files will take a local path as an argument (and it will use the same mapping logic that you're trying to reverse-engineer to figure out what depot path it goes with, while also handling a bunch of edge cases that you haven't considered).

I don't have a .NET dev env set up, but I'm pretty sure if it works for "p4 fstat" it'll work for GetFileMetadata():

C:\Perforce\test>p4 fstat C:\Perforce\test\foo
... depotFile //stream/main/foo
... clientFile c:\Perforce\test\foo
... isMapped
... headAction edit
... headType text
... headTime 1568160303
... headRev 4
... headChange 119
... headModTime 1543246621
... haveRev 4


#4 Lubos Suk

Lubos Suk

    Newbie

  • Members
  • Pip
  • 5 posts

Posted 16 January 2020 - 08:01 AM

View PostSambwise, on 07 January 2020 - 03:44 PM, said:

If your intent is to figure out which depot path a given client path maps to, why not just pass the client path directly to GetFileMetadata or whatever method you're ultimately trying to pass the depot path to?  Almost every Perforce command that operates on depot files will take a local path as an argument (and it will use the same mapping logic that you're trying to reverse-engineer to figure out what depot path it goes with, while also handling a bunch of edge cases that you haven't considered).

I don't have a .NET dev env set up, but I'm pretty sure if it works for "p4 fstat" it'll work for GetFileMetadata():

C:\Perforce\test>p4 fstat C:\Perforce\test\foo
... depotFile //stream/main/foo
... clientFile c:\Perforce\test\foo
... isMapped
... headAction edit
... headType text
... headTime 1568160303
... headRev 4
... headChange 119
... headModTime 1543246621
... haveRev 4

First but not last scenario is to Rename/Move Files.

https://www.perforce...t_MoveFiles.htm

This method accepts only FileSpec that first is current file and second is new filename/location. And if you try GetFileMetadata (fstat) on non-existing file you will error. But when you use getFileMappings (where) it will returns FileSpec that is needed.

#5 Sambwise

Sambwise

    Advanced Member

  • Members
  • PipPipPip
  • 1175 posts

Posted 16 January 2020 - 04:12 PM

View PostLubos Suk, on 16 January 2020 - 08:01 AM, said:

First but not last scenario is to Rename/Move Files.

https://www.perforce...t_MoveFiles.htm

This method accepts only FileSpec that first is current file and second is new filename/location.

Just pass the client path directly to the move command.

C:\Perforce\test\move>p4 edit foo
//stream/main/move/foo#1 - opened for edit

C:\Perforce\test\move>p4 move foo bar
//stream/main/move/bar#1 - moved from //stream/main/move/foo#1

I can't emphasize enough that in just about every situation Perforce will simply automatically translate client paths to depot paths.  That's the entire point of having a client view; it's not something that's there to make you do annoying extra steps.  :)  Any time you see a command that takes a "filespec", you SHOULD be able to pass either a depot path or a client path to it.

I think in the .NET context that looks something like this?  (I don't use C# at all so my syntax is probably wrong, I'm just looking at the docs)

MoveFiles(FileSpec(ClientPath("foo")), FileSpec(ClientPath("bar")))

i.e. when you build your FileSpec out of a PathSpec, use a ClientPath as the PathSpec instead of a DepotPath.

The same applies to, say, the "p4 add" command, where there is no depot file (so "p4 fstat" will return nothing); the server will use your client view to automatically translate the local path you give it into the appropriate depot path:


C:\Perforce\test\move>p4 add -n ola
//stream/main/move/ola#1 - opened for add

The only situations where you need to use fully qualified depot paths are the ones where there is no client mapping at all (in which case doing the stuff with "p4 where" will get you no result anyway) or where you need to override the client view (e.g. you have some tricky overlay mapping and you want to "p4 add" into an ambiguous non-default path -- your code doesn't handle that case either because it assumes that mappings are non-ambiguous).  Actually parsing client views is something that you should simply never need to do unless you're doing something VERY specialized (like trying to debug client views themselves, or write a UI for building client views, or something like that).

Note that when the server resolves a local path into a depot path, it will do things that your current approach isn't accounting for.  The client view might have changed since the file was synced (in which case the server will use the client sync records, aka the "have table", to figure out where the file came from), or there might be an overlay mapping (in which case there are multiple potential depot file mappings and the server will check all of them and return the first extant match).  It's much better to simply let the server handle this than to try to reverse-engineer it and slowly work your way through all the edge cases that Perforce's server engineers have already handled over the course of more than two decades.  :)





Also tagged with one or more of these keywords: where, p4api.net, branch

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users