How to get specific version of folder from tfs without creating a workspace?
Asked Answered
L

4

11

I would like to get the source code of a project at specific time (changeset). So I need do download whole folder. I would like to do it for different times and handling a different workspace is not very convenient.

I know about TFS Get Specific Version into separate folder (with workspace) and Need command to get a file from TFS without a workspace (one file).

Is there some solution for whole folder without creating a new workspace?

Edit I have found the accepted answer too ambitious. I needed something more simple.

Assumptions:

  • I can access TFS from Visual Studio on my computer
  • I want to get the ChangeSetNumber Changeset from the folder DesiredFolder in TFS project tProj

I run the following batch from a destination folder in Visual Studio Command Prompt

set workspace_name=TemporaryWorkspace%username%
set changeset= ChangeSetNumber                
tf workspace -new %workspace_name% -noprompt
tf workfold -map $/tProj . -workspace:%workspace_name%
tf get $/tProj/DesiredFolder -version:C%changeset% -recursive -noprompt
tf workfold -unmap . -workspace:%workspace_name%
tf workspace -delete %workspace_name% -noprompt

It is necessary to confirm remove source control association when starting the downloaded solution.

Edit 2023 I wanted to use the script but I was getting the following error:

Unable to determine the workspace.

I managed to find a solution in SO:Unable to determine the workspace using TF.exe. And from it I have a corrected script:

set workspace_name=TemporaryWorkspace%username%
set changeset=ChangeSetNumber
tf workspace -new %workspace_name% -noprompt /collection:<team-project-collection-url>
tf workfold -map $/ . -workspace:%workspace_name%
tf get $/tProj/DesiredFolder -version:C%changeset% -recursive -noprompt
tf workfold -unmap . -workspace:%workspace_name%
tf workspace -delete %workspace_name% -noprompt

Where <team-project-collection-url> is the URL of the project collection that contains the workspace that you want to create, for example, https://myserver:8080/tfs/DefaultCollection.

Probably something has changed during past five or six versions of Visual Studio

Lumpkin answered 3/5, 2012 at 13:41 Comment(4)
Can you explain/elaborate "to do it for different times" ?Personable
@Nockawa: Maybe there should be different changesets. I just need to study whether and why it behaves different way than that used to.Lumpkin
Can you explain why manipulation with a workspace is not convenient? If you have a workspace set up and you want to sync the whole project to changeset 150 you can do this with one command: "tf get /version:150".Intelligencer
@TaylorLafrinere: I have workspace synchronized with current development branch, with branch for previous version. No I need something temporary and preferably independent on tfs to study older version behaviour. After task is finished I delete. If I wonted something other, I would downloaded to the other folder. There should be no backward path to Tfs. But maybe I don't uderstand workspace paradigm correctly.Lumpkin
C
16

I use this syntax for temporary workspaces:

tf workspace -new %JOB_NAME%;%user% -noprompt -server:http://%host%:8080/tfs/%project% -login:%user%,%password%
tf workfold -map $/Release/MilestoneX.X . -workspace:%JOB_NAME% -server:http://%host%:8080/tfs/%project% -login:%user%,%password%
tf get . -version:L%TFS_LABEL% -recursive -noprompt -login:%user%,%password%
tf workfold -unmap . -workspace:%JOB_NAME% -login:%user%,%password%
tf workspace -delete %JOB_NAME%;%user% -noprompt -server:http://%host%:8080/tfs/%project% -login:%user%,%password%
Casualty answered 9/5, 2012 at 21:14 Comment(0)
H
8

I've discovered that you can do this through the HTTP api that TFS exposes. The "signature" for the URL is as follows:

http(s)://{server}:{port}/tfs/{collectionName}/{teamProjectName}/_api/_versioncontrol/itemContentZipped?version={versionSpec}&path={escapedPathToFolder}

So, if you have a project named "MyProject" in the DefaultCollection, and want to get the content of a folder called "MyFeature":

http://MyTfsServer:8080/tfs/DefaultCollection/MyProject/_api/_versioncontrol/itemContentZipped?version=C1001&path=%24%2FMyProject%2FMyFeature

I think "version" can be any version spec, which is documented in the TFS API documentation. My example is requesting the version as of change set 1001. I was using the .NET API to get a specific version, which is pretty straightforward, but slow because it can only get one file at a time. I'm trying to figure out if this same functionality is exposed through the .NET API because downloading the files this way is much much faster than getting a single file at a time.

I implemented this as an extension method on Microsoft.TeamFoundation.VersionControl.Client.Item. This returns a stream that contains a zip file. I had used this as part of a custom MSBuild task which then saves the contents of this stream to a file location.

public static class TfsExtensions
{

    const String ItemContentZippedFormat = "/_api/_versioncontrol/itemContentZipped?version={0}&path={1}&__v=3";

    public static Stream DownloadVersion(this Item folder, VersionSpec version)
    {
        if (folder.ItemType != ItemType.Folder)
            throw new ArgumentException("Item must be a folder", "folder");


        var vcs = folder.VersionControlServer;

        var collectionName = vcs.TeamProjectCollection.CatalogNode.Resource.DisplayName;

        var baseUri = folder.VersionControlServer.TeamFoundationServer.Uri;
        if (!baseUri.LocalPath.EndsWith(collectionName, StringComparison.OrdinalIgnoreCase))
            baseUri = new Uri(baseUri, baseUri.LocalPath + "/" + collectionName);


        var apiPath = String.Format(ItemContentZippedFormat, version.DisplayString, WebUtility.UrlEncode(folder.ServerItem));
        var downloadUri = new Uri(baseUri, baseUri.LocalPath + apiPath);

        var req = WebRequest.Create(downloadUri);
        req.Credentials = CredentialCache.DefaultCredentials;
        var response = req.GetResponse();
        return response.GetResponseStream();
    }
}
Hoxsie answered 5/11, 2013 at 0:21 Comment(4)
Do you communicate with a tfs server with WebClient or the other way? Would you add a sample code?Lumpkin
@Lumpkin I use System.Net.WebRequest to get the content. Included an example.Hoxsie
Note that using this sort of a mechanism will not take advantage of download proxies, if you have those configured. Otherwise, this is a very nice mechanism indeed.Bindman
The ".zip download from web address" approach produced the following results here: for the $\Main folder it download a Main.zip folder with a completely empty Main folder inside it; for $\Main\SomeFile.ext it gave error No matching items found in $/Main/SomeFile.ext at the specified version, or you do not have permission to access them.Clypeate
P
5

I think you should create a temporary Workspace to retrieve the content you want, then delete the Workspace and keep the local items.

A Workspace in TFS is a local view of what's on the server, for a given Workspace you choose which folder(s) you want to retrieve locally and where you'll store the folders/files.

It's not like SourceSafe you're not bound to only one workspace, you can have as many as you want on a given computer.

So I suggest you to create a dedicated Workspace for the operation you want to do and get rid of it when you judge it appropriate.

Use the TF.exe workspace command to create/delete a Workspace from the Shell. Then TF.exe get to retrieve the files.

Personable answered 3/5, 2012 at 19:33 Comment(1)
I'll give it a chance. But I would like to have something as view but for a folder.Lumpkin
T
1

You can use tf view to get a specific file without creating a workspace.

Retrieves a specific version of a file to a temporary folder on your computer and displays it.

tf vc view [/collection:TeamProjectCollectionUrl]
       [/console] [/recursive] [/output:localfile]
       [/shelveset:shelvesetname[;owner]] [/noprompt] itemspec
       [/version:versionspec] [/login:username,[password]]

Versionspec:
Date/Time         D"any .Net Framework-supported format"
                  or any of the date formats of the local machine
Changeset number  Cnnnnnn
Label             Llabelname
Latest version    T
Workspace         Wworkspacename;workspaceowner
Thundersquall answered 13/8, 2015 at 15:33 Comment(2)
This approach only works with individual files, not with directories. Running the command as you have it here will produce one massive file (named C:\path\to\localdirectory) that contains every file in the folder concatenated together.Derzon
@JamesQMurphy, thanks! I can't believe that's how that works. I ended up solving this with a powershell function using a combination of dir and view. You can see it in this gist.Thundersquall

© 2022 - 2024 — McMap. All rights reserved.