41

I'm working on a utility processing files being under source control using TFS 2010.

If an item is not yet checked-out for edit, I'm getting an exception, what is definitely predictable because file is in read-only mode.

What ways exist to check-out a file?

P.S. I want something for programmatic rather then Process.Start("tf.exe", "..."); if that's applicable.

Zoe
  • 23,712
  • 16
  • 99
  • 132
abatishchev
  • 92,232
  • 78
  • 284
  • 421

6 Answers6

39

Some of the other approaches mentioned here only work for certain versions of TFS or make use of obsolete methods. If you are receiving a 404, the approach you are using is probably not compatible with your server version.

This approach works on 2005, 2008, and 2010. I don't use TFS any longer, so I haven't tested 2013.

var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(fileName);
using (var server = new TfsTeamProjectCollection(workspaceInfo.ServerUri))
{
    var workspace = workspaceInfo.GetWorkspace(server);    
    workspace.PendEdit(fileName);
}
Ben
  • 2,785
  • 21
  • 31
  • I deleted my answer because yours is a simplified version that works with VS 2010 as well. It turns out you don't have to go through the `TfsConfigurationServer` to get the objects you need. http://blogs.msdn.com/b/taylaf/archive/2010/02/23/introducing-the-tfsconnection-tfsconfigurationserver-and-tfsteamprojectcollection-classes.aspx – RandomEngy Jun 12 '12 at 17:45
  • 1
    `TfsTeamProjectCollection` is IDisposable, so you might want to wrap it in a using block – Carl Walsh Apr 18 '15 at 17:54
  • You're right. The code I copied this from kept the `TfsTeamProjectCollection` around for multiple uses. But this improves the example. – Ben May 05 '15 at 19:35
  • Wanted to add a comment for any future visitors. If you're using the more verbose `PendEdit()` function that takes in the `silent` parameter, you want to set `silent` to `false`, this will make it remove the read-only lock. – njkremer Oct 17 '16 at 15:46
6
private const string tfsServer = @"http://tfsserver.org:8080/tfs";

public void CheckOutFromTFS(string fileName)
{
    using (TfsTeamProjectCollection pc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsServer)))        
    {
        if (pc != null)
        {
            WorkspaceInfo workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(fileName);
            if (null != workspaceInfo)
            {                   
                Workspace workspace = workspaceInfo.GetWorkspace(pc);
                workspace.PendEdit(fileName);
            }
        }
    }
    FileInfo fi = new FileInfo(fileName);
}

Note that Microsoft.TeamFoundation.Client.TeamFoundationServerFactory is obsolete: The TeamFoundationServer class is obsolete. Use the TeamFoundationProjectCollection or TfsConfigurationServer classes to talk to a 2010 Team Foundation Server. In order to talk to a 2005 or 2008 Team Foundation Server use the TeamFoundationProjectCollection class. The corresponding factory class for that is the TfsTeamProjectCollectionFactory.

abatishchev
  • 92,232
  • 78
  • 284
  • 421
wp9omp
  • 61
  • 1
  • 1
4

You can use Team Foundation Version Control client API. The method is PendEdit()

workspace.PendEdit(fileName);

Checkout detailed example on MSDN http://blogs.msdn.com/b/buckh/archive/2006/03/15/552288.aspx

ukhardy
  • 2,054
  • 1
  • 13
  • 12
2

First get the workspace

var tfs = new TeamFoundationServer("http://server:8080/tfs/collection");
var version = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
var workspace = version.GetWorkspace("WORKSPACE-NAME", version.AuthorizedUser);

With the workspace you can checkout the file

workspace.PendEdit(fileName);
BrunoLM
  • 88,362
  • 76
  • 272
  • 427
1
var registerdCollection = RegisteredTfsConnections.GetProjectCollections().First();
var projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(registerdCollection);
var versionControl = projectCollection.GetService<VersionControlServer>();

var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(_fileName);
var server = new TeamFoundationServer(workspaceInfo.ServerUri.ToString());
var workspace = workspaceInfo.GetWorkspace(server);

workspace.PendEdit(fileName);
abatishchev
  • 92,232
  • 78
  • 284
  • 421
0

I have two approaches how to do that: simple and advanced.

1). Simple:

  #region Check Out
    public bool CheckOut(string path)
    {
        using (TfsTeamProjectCollection pc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(ConstTfsServerUri)))
        {
            if (pc == null) return false;

            WorkspaceInfo workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(path);
            Workspace workspace = workspaceInfo?.GetWorkspace(pc);
            return workspace?.PendEdit(path, RecursionType.Full) == 1;
        }
    }

    public async Task<bool> CheckoutAsync(string path)
    {
        return await Task.Run(() => CheckOut(path));
    }
    #endregion

2). Advanced (with receiving status):

    private static string GetOwnerDisplayName(PendingSet[] pending)
    {
        var result = pending.FirstOrDefault(pendingSet => pendingSet.Computer != Environment.MachineName) ?? pending[0];
        return result.OwnerDisplayName;
    }
    private string CheckoutFileInternal(string[] wsFiles, string folder = null)
    {
        try
        {

            var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(folder);
            var server = new TfsTeamProjectCollection(workspaceInfo.ServerUri);
            var workspace = workspaceInfo.GetWorkspace(server);
            var request = new GetRequest(folder, RecursionType.Full, VersionSpec.Latest);
            GetStatus status = workspace.Get(request, GetOptions.None);
            int result = workspace.PendEdit(wsFiles, RecursionType.Full, null, LockLevel.None);
            if (result == wsFiles.Length)
            {
                //TODO: write info (succeed) to log here - messageText
                return null;
            }
            var pending = server.GetService<VersionControlServer>().QueryPendingSets(wsFiles, RecursionType.None, null, null);
            var messageText = "Failed to checkout !.";
            if (pending.Any())
            {
                messageText = string.Format("{0}\nFile is locked by {1}", messageText, GetOwnerDisplayName(pending));
            }

            //TODO: write error to log here - messageText
            return messageText;
        }
        catch (Exception ex)
        {
            UIHelper.Instance.RunOnUiThread(() =>
            {
                MessageBox.Show(Application.Current.MainWindow, string.Format("Failed checking out TFS files : {0}", ex.Message), "Check-out from TFS",
                               MessageBoxButton.OK, MessageBoxImage.Error);
            });
            return null;
        }
    }

    public async Task<string> CheckoutFileInternalAsync(string[] wsFiles, string folder)
    {
        return await Task.Run(() => CheckoutFileInternal(wsFiles, folder));
    }
Mr.B
  • 2,848
  • 20
  • 34
  • You may want to suffix the internal function internal and remove that suffix from the public one. – abatishchev Jul 04 '16 at 15:24
  • Also running sync function in async manner doesn't make much sense to me. Anyway you can probably return task directly rather than await it so it will be awaited caller – abatishchev Jul 04 '16 at 15:25