I'm writing a TFS plugin to automate merging of changesets related to a work item whenever said work item is changed from state "Resolved" to state "Closed". The following code is what I have so far:
C#
private void Action_ResolvedToClosed()
{
//Linq query for getting changesets associated with the current work item
var changeSets = WorkItem.Links
.OfType<ExternalLink>()
.Select(link =>
VersionControlServer.ArtifactProvider.GetChangeset(new Uri(link.LinkedArtifactUri))).ToList();
if (!changeSets.Any())
{
LOG_NOCHANGESETS(WorkItem.Id);
return;
}
Workspace workspace = VersionControlServer.GetWorkspace(<My Workspace>);
var source = URI_LOCAL; // $/<Project Name>/<Working Branch>
var destination = URI_DEV; // $/<Project Name>/<Development Branch>
// Merge applicable changesets
foreach (var versionSpec in changeSets.Select(changeset => new ChangesetVersionSpec(changeset.ChangesetId)))
{
workspace.Merge(source, destination, versionSpec, versionSpec);
workspace.CheckIn(workspace.GetPendingChanges(), "**Automated Merge**");
LOG_SUCCESS(versionSpec.ChangesetId, WorkItem.Id);
}
}
Is there a way to dynamically generate the workspace variable? Odds are I won't be the one actually making changes - the goal is to automate this process for our devs.
UPDATE: I'm pretty sure what I'm looking for in this second part is GetStatus, so it can be ignored. The paragraph above is my real question.
Secondary: I feel like automating merges can't be this simple. What happens if merge conflicts arise? Does Workspace.Merge fail gracefully? Are there any other glaring issues that someone with a bit more experience with the TFS API can point out?
First, it sounds like you want to query the user's workspace cache to get the appropriate Workspace and "realize" it. If
WorkspaceInfo workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(path);
Workspace workspace = workspaceInfo.GetWorkspace(new TfsTeamProjectCollection(workspaceInfo.ServerUri);
However, as you point out, you may prefer to create a temporary workspace. This will ensure that you do not conflict with any changes the user is trying to make in their own workspace. For example:
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri("http://server:8080/tfs/DefaultCollection"));
VersionControlServer vcs = tpc.GetService<VersionControlServer>();
Workspace workspace = vcs.CreateWorkspace("MERGE-TEMP");
workspace.Map("$/Merge-Source", #"C:\Temp\Merge\Source");
workspace.Map("$/Merge-Target", #"C:\Temp\Merge\Target");
Second, if you run into merge conflicts, they will be set in the workspace. You can query the NumConflicts method of the returned GetStatus. (Though you will not be able to Checkin untli you have resolved the conflicts.
Related
I want to retrieve a list of all branches of a TFS workspace which are mapped locally. I already got a solution where I retrieve all branches of a VersionControlServer-Object, but that`s not what I want to get here. It should be a list specific for my workspace.
var branchObjects = m_VersionControlServer.QueryRootBranchObjects(RecursionType.Full);
List<string> branches = new List<string>();
foreach (var branch in branchObjects)
{
var branchName = branch.Properties.RootItem.Item;
branches.Add(branchName);
}
Do u got any ideas how to check which of the branches where mapped at the local workspace? An instance of the specific workspace-class is available.
You can achieve this with the TFS tf command line tool
tf workspaces /owner:* /computer:* /collection:https://tfs.yourdomain.com/DefaultCollection /format:xml
If you don't have tf.exe, refer to this page How to get tf.exe (TFS command line client)?
Assuming you know the local path of your workspace you can use:
var workspace = versionControlServer.TryGetWorkspace(...path...)
Or you can use the Workstation class to query local workspaces on your machine.
Then from the workspace you can get the workspaces from the QueryWorkspaceInfo method and from there the mappings WorkspaceInfo.Mappings property. And from there you can check whether your branchroots (which you've already got figured out) are mapped in any of the workspaces on the server. If you want to be able to look up server path, you'll need to call the WorkspacnInfo.GetWorkspace method and from there use the Workspace.Folders property.
I am able to successfully fetch, pull, push, etc. using LibGit2Sharp, but I would like to be able to list files that have changed, added, etc. after doing a fetch. I'm using https://github.com/libgit2/libgit2sharp/wiki/git-fetch and no errors or exceptions occur and logMessage is an empty string.
I would like to be able to show a list of changes like Visual Studio does when you perform a fetch.
How can I use LibGit2Sharp to accomplish this?
Edit:
I have read through the LibGit2Sharp Wiki and the LibGit2Sharp Hitchhiker's Guide to Git. While I have tried some of the available commands to review what results they offer, I am not sure what the equivalent git command would be for this either. It would be helpful to know and understand which command would provide this information and would be appreciated if you are familiar with Git, but not LibGit2Sharp.
Once the fetch is done, you can list the fetched commit of a given branch with
git log ..#{u}
with #{u} designating the branch you are merging from (the upstream remote tracking branch, generally origin/yourbranch)
In LibGitSharp, that is what LibGit2Sharp/BranchUpdater.cs#UpstreamBranch reference (the upstream branch)
With that, you should be able to list the commmits between your current branch HEAD and "UpstreamBranch", a bit like in issue 1161, but that issue was listing what is being pushed: let's invert the log parameters here.
var trackingBranch = repo.Head.TrackedBranch;
var log = repo.Commits.QueryBy(new CommitFilter
{ IncludeReachableFrom = trackingBranch.Tip.Id, ExcludeReachableFrom = repo.Head.Tip.Id });
var count = log.Count();//Counts the number of log entries
//iterate the commits that represent the difference between your last
//push to the remote branch and latest commits
foreach (var commit in log)
{
Console.WriteLine(commit.Message);
}
Maybe asked before, but I cannot find the clear answer. When I need to list the branches of a repo with gitlib2sharp, do I really first need to clone to a local repo? What is the sense in that? I just want to clone a specific branch, like you do with git clone https://bla/repo.git -b branch1
Now I first need to do a local checkout, then get the branches and from there do a second round.
Am I missing something here (hope I do).
TIA for your answer!
Grtz,
Ronald
Update (to long for comment):
OK, so here's the use case. A company delivers ssrs reports, which we need to deploy through TAP. So my thoughts were to do this via Git. For each change let them create a branche, upload/alter/etc in this branche. And iterate changes in this branch till all is fine. In the iterations they should be able to (re)deploy themselves on at least T. At the final stage we merge the brnanch to master and (re)deploy master in P. In Github you can completely 'click' this flow, but of course I want to automate this and have preferably someelse push the buttons, so they don't need me for this. So what's the best programmatic choice here? When they make a branch and start deploying in T, should I create (clone) the repo, point my local repo to the specific branch, get the files (.sql and .rdl files) and execute/upload these? I was not aware that when you clone a repo, you clone all the branches with it. Thanks so far already!
If you want to do something like git clone https://bla/repo.git -b branch1 with libgtk2sharp try this one:
var exampleRepositoryUrl = "https://github.com/npgsql/npgsql.git";
var exampleDestinationFolder = "branch-hotfix-3.0.8";
var exampleBranchName = "hotfix/3.0.8";
var repositoryClonedPath = Repository.Clone(exampleRepositoryUrl,
exampleDestinationFolder,
new CloneOptions()
{
BranchName = exampleBranchName
});
using (var clonedRepo = new Repository(repositoryClonedPath))
{
// ...
}
To list names of remote branches without cloning a repo you can use something like this:
var branches = Repository.ListRemoteReferences(exampleRepositoryUrl)
.Where(elem => elem.IsLocalBranch)
.Select(elem => elem.CanonicalName
.Replace("refs/heads/", ""));
PiKos answer worked for me, but I had to set a credentials handler, e.g.:
var branches = Repository.ListRemoteReferences(exampleRepositoryUrl, MyCredentialsHandler)
.Where(elem => elem.IsLocalBranch)
.Select(elem => elem.CanonicalName
.Replace("refs/heads/", ""));
For more details on how to set a custom credentials handler, see this answer:
https://stackoverflow.com/a/55371988/5507590
I am trying to get the local workspace to sync up with the remote workspace. The remote workspace is WindowsMain/MainProject/Subproject and the local is C:/MainProject/Subproject the problem is when I run the code shown below it says that the local workspace isn't mapped. What am I doing wrong that makes it so that the code isn't found? (localpath is C:/MainProject/Subproject
TfsTeamProjectCollection tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://remoteserver:8080/tfs_proj/WindowsMain"), new UICredentialsProvider());
tfs.EnsureAuthenticated();
VersionControlServer vsStore = tfs.GetService<VersionControlServer>();
Workspace workspace = vsStore.GetWorkspace(localpath);
I'm not sure what is that you want exactly,
...but this is the code I used (and I remember having trouble finding the right solution at the time):
teamProjectCollection = new TfsTeamProjectCollection(new Uri(collectionUri), new TfsClientCredentials());
teamProjectCollection.Authenticate();
versionControl = teamProjectCollection.GetService<VersionControlServer>();
(note: the credentials like this are automatically picked from what
you use in VS)
then I get all the workspaces (in your case it may be just one) - that helps to get the right matching paths for server and client (the Workstation class does the same, if that doesn't work for some reason, this does)
versionControl.QueryWorkspaces(null, null, computerName)
.SelectMany(x => x.Folders); // returns IEnumerable<WorkingFolder>
pick the workspace you need and you have both local and server path
LocalSourcePath = folder.LocalItem;
ServerPath = folder.ServerItem;
...and then I use the ServerPath and the versionControl to actually download the files (and e.g. you can check them against the LocalSourcePath)...
foreach (Item item in
versionControl.GetItems(serverPath, VersionSpec.Latest, RecursionType.Full, DeletedState.NonDeleted, ItemType.Any, true).Items)
{
string target = Path.Combine(downloadPath, item.ServerItem.Substring(2));
if (item.ItemType == ItemType.Folder && !Directory.Exists(target))
{
Directory.CreateDirectory(target);
}
else if (item.ItemType == ItemType.File)
{
item.DownloadFile(target);
}
}
...this is probably slightly different for your case but you should be able to work out the specific details from this, hope it helps.
According to you code, you need to pass through a VersionControlServer object, and to obtain such an object you need to know the address of the Tfs server the workspace is mapped to.
If you can determine the physical path of the solution or project file, then you can query that file in TFS and you should see which workspace has been mapped to that local file location. Note: The mapped path for a workspace had to be unique.
Moreover, there is a more powerful way though, using the Workstation class. Ricci Gian Maria has written a quite extensive blog post about this topic.
Use the Workstation class to get the WorkspaceInfo for the path you're looking for, this will search the workpaces for all TFS servers registered on that workstation to see if there's a match:
Workstation workstation = Workstation.Current;
WorkspaceInfo info = workstation.GetLocalWorkspaceInfo(path);
More details please refer his blog: How to get TFS server address from a local folder mapped to a workspace
I am wondering if there is a way for me to list all 'files' containted in a tfs team project. What I am aiming to do is search for files of a particular name that dont have fixed paths within TFS caused by branching ($/MyTeamProject/Main/Build/instruction.xml and $/MyTeamProject/Branches/Release_1.0). Once a file would be found I would like to manipulate it.
I guess that we are talking items when it comes to entities containted within a team project and not traditional files and therefore this might be a bit tricky?
I have seen some samples for manipulating a file but all samples so far have fixed paths.
This is not a different answer, but simply an upgrade of Vengafoo's code. The TeamFoundationServer class is obsolete in 2011 (not sure when this happened, I just know it is obsolete as of now). Vengafoo's code is from 2009, so that makes sense. Use the TfsTeamProjectCollection class with the TfsTeamProjectCollectionFactory factory class instead.
Here is the upgrade, just one line of change:
//TeamFoundationServer server = new TeamFoundationServer("server");
TfsTeamProjectCollection server = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://tfsServerURI:8080/tfs/"));
VersionControlServer version = server.GetService(typeof(VersionControlServer)) as VersionControlServer;
ItemSet items = version.GetItems(#"$\ProjectName", RecursionType.Full);
//ItemSet items = version.GetItems(#"$\ProjectName\FileName.cs", RecursionType.Full);
foreach (Item item in items.Items)
{
System.Console.WriteLine(item.ServerItem);
}
Here is how I've figured out how to list all the files of a TFS Project:
Add Microsoft.TeamFoundation.Client and Microsoft.TeamFoundation.VersionControl.Client as a reference to your project.
Add a using for Microsoft.TeamFoundation.Client and Microsoft.TeamFoundation.VersionControl.Client
TeamFoundationServer server = new TeamFoundationServer("server");
VersionControlServer version = server.GetService(typeof(VersionControlServer)) as VersionControlServer;
ItemSet items = version.GetItems(#"$\ProjectName", RecursionType.Full);
ItemSet items = version.GetItems(#"$\ProjectName\FileName.cs", RecursionType.Full);
foreach (Item item in items.Items)
{
System.Console.WriteLine(item.ServerItem);
}
The second GetItems will restrict the items found to those of a specific filename. I just have this sample outputting the server path for all of the files found.