prep for online patch
[tfs.git] / class / Microsoft.TeamFoundation.VersionControl.Client / Workspace.cs
blob715172160e9b8aeb44f4bdee3ef84653d96ef6f3
1 //
2 // Microsoft.TeamFoundation.VersionControl.Client.Workspace
3 //
4 // Authors:
5 // Joel Reed (joelwreed@gmail.com)
6 //
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System;
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Net;
33 using System.Text;
34 using System.Xml;
35 using System.Web.Services;
36 using Microsoft.TeamFoundation.VersionControl.Common;
38 namespace Microsoft.TeamFoundation.VersionControl.Client
40 public sealed class Workspace : IComparable
42 private string comment;
43 private string computer;
44 private string name;
45 private string ownerName;
46 private DateTime lastAccessDate;
47 private WorkingFolder[] folders;
48 private VersionControlServer versionControlServer;
50 internal Workspace(VersionControlServer versionControlServer, string name,
51 string ownerName, string comment,
52 WorkingFolder[] folders, string computer)
54 this.versionControlServer = versionControlServer;
55 this.name = name;
56 this.ownerName = ownerName;
57 this.comment = comment;
58 this.folders = folders;
59 this.computer = computer;
62 public int CheckIn(PendingChange[] changes, string comment)
64 if (changes.Length == 0) return 0;
66 List<string> serverItems = new List<string>();
67 foreach (PendingChange change in changes)
69 // upload new or changed files only
70 if ((change.ItemType == ItemType.File) &&
71 (change.IsAdd || change.IsEdit ))
73 Repository.UploadFile(Name, OwnerName, change);
74 File.SetAttributes(change.LocalItem, FileAttributes.ReadOnly);
77 serverItems.Add(change.ServerItem);
80 return Repository.CheckIn(this, serverItems.ToArray(), comment);
83 public int CompareTo (object o)
85 Workspace a = this, b = (Workspace)o;
87 int r0 = a.VersionControlServer.ServerGuid.CompareTo (b.VersionControlServer.ServerGuid);
88 if (r0 != 0) return r0;
90 int r1 = a.Name.CompareTo (b.Name);
91 if (r1 != 0) return r1;
93 int r2 = a.OwnerName.CompareTo (b.OwnerName);
94 return r2;
97 public PendingChange[] GetPendingChanges()
99 return GetPendingChanges(VersionControlPath.RootFolder, RecursionType.Full);
102 public PendingChange[] GetPendingChanges(string item)
104 return GetPendingChanges(item, RecursionType.None);
107 public PendingChange[] GetPendingChanges(string item, RecursionType rtype)
109 return GetPendingChanges(item, rtype, false);
112 public PendingChange[] GetPendingChanges(string item, RecursionType rtype,
113 bool includeDownloadInfo)
115 string[] items = new string[1];
116 items[0] = item;
117 return GetPendingChanges(items, rtype, includeDownloadInfo);
120 public PendingChange[] GetPendingChanges(string[] items, RecursionType rtype)
122 return GetPendingChanges(items, rtype, false);
125 public PendingChange[] GetPendingChanges(string[] items, RecursionType rtype,
126 bool includeDownloadInfo)
128 List<ItemSpec> itemSpecs = new List<ItemSpec>();
129 foreach (string item in items)
131 itemSpecs.Add(new ItemSpec(item, rtype));
134 Failure[] failures = null;
135 PendingChange[] changes = Repository.QueryPendingSets(Name, OwnerName, Name, OwnerName,
136 itemSpecs.ToArray(), includeDownloadInfo,
137 out failures);
138 foreach (Failure failure in failures)
140 Console.WriteLine(failure.ToString());
143 return changes;
146 public void Delete()
148 Repository.DeleteWorkspace(Name, OwnerName);
149 Workstation.Current.RemoveCachedWorkspaceInfo(VersionControlServer.Uri, Name);
152 public GetStatus Get()
154 return Get(VersionSpec.Latest, GetOptions.None);
157 public GetStatus Get(VersionSpec versionSpec, GetOptions options)
159 GetRequest request = new GetRequest(versionSpec);
160 return Get(request, GetOptions.None, null, null);
163 public GetStatus Get(GetRequest request, GetOptions options)
165 return Get(request, options, null, null);
168 public GetStatus Get(GetRequest[] requests, GetOptions options)
170 return Get(requests, options, null, null);
173 public GetStatus Get(GetRequest request, GetOptions options,
174 GetFilterCallback filterCallback, object userData)
176 GetRequest[] requests = new GetRequest[1];
177 requests[0] = request;
178 return Get(requests, options, filterCallback, userData);
181 public GetStatus Get (string[] items, VersionSpec version,
182 RecursionType recursion, GetOptions options)
184 List<GetRequest> requests = new List<GetRequest>();
185 foreach (string item in items)
187 requests.Add(new GetRequest(item, recursion, version));
190 return Get(requests.ToArray(), options, null, null);
193 public GetStatus Get(GetRequest[] requests, GetOptions options,
194 GetFilterCallback filterCallback, object userData)
196 bool force = ((GetOptions.Overwrite & options) == GetOptions.Overwrite);
197 bool noGet = ! ((GetOptions.GetAll & options) == GetOptions.GetAll);
199 GetOperation[] getOperations = Repository.Get(Name, OwnerName, requests, force, noGet);
200 if (null != filterCallback) filterCallback(this, getOperations, userData);
202 UpdateLocalVersionQueue updates = new UpdateLocalVersionQueue(this);
203 foreach (GetOperation getOperation in getOperations)
205 string uPath = null;
206 GettingEventArgs args = new GettingEventArgs(this, getOperation);
208 Console.WriteLine(getOperation.ToString());
210 if (getOperation.DeletionId != 0)
212 if ((getOperation.ItemType == ItemType.Folder)&&
213 (Directory.Exists(getOperation.SourceLocalItem)))
215 Directory.Delete(getOperation.SourceLocalItem, true);
217 else if ((getOperation.ItemType == ItemType.File)&&
218 (File.Exists(getOperation.SourceLocalItem)))
220 File.SetAttributes(getOperation.SourceLocalItem, FileAttributes.Normal);
221 File.Delete(getOperation.SourceLocalItem);
224 else if ((!String.IsNullOrEmpty(getOperation.TargetLocalItem))&&
225 (!String.IsNullOrEmpty(getOperation.SourceLocalItem))&&
226 (getOperation.SourceLocalItem != getOperation.TargetLocalItem))
228 uPath = getOperation.TargetLocalItem;
231 File.Move(getOperation.SourceLocalItem, getOperation.TargetLocalItem);
233 catch (IOException)
235 args.Status = OperationStatus.TargetIsDirectory;
238 else if (getOperation.ChangeType == ChangeType.None)
240 uPath = getOperation.TargetLocalItem;
241 string directory = uPath;
243 if (getOperation.ItemType == ItemType.File)
244 directory = Path.GetDirectoryName(uPath);
246 if (!Directory.Exists(directory))
247 Directory.CreateDirectory(directory);
249 if (getOperation.ItemType == ItemType.File)
250 DownloadFile.WriteTo(uPath, Repository, getOperation.ArtifactUri);
253 versionControlServer.OnDownloading(args);
254 updates.QueueUpdate(getOperation.ItemId, uPath, getOperation.VersionServer);
257 updates.Flush();
258 return new GetStatus(getOperations.Length);
261 public ExtendedItem[][] GetExtendedItems (ItemSpec[] itemSpecs,
262 DeletedState deletedState,
263 ItemType itemType)
265 return Repository.QueryItemsExtended(Name, OwnerName,
266 itemSpecs, deletedState, itemType);
269 public string GetServerItemForLocalItem(string localItem)
271 string item = TryGetServerItemForLocalItem(localItem);
272 if (item == null)
273 throw new ItemNotMappedException(localItem);
274 return item;
277 public string GetLocalItemForServerItem(string serverItem)
279 string item = TryGetLocalItemForServerItem(serverItem);
280 if (item == null)
281 throw new ItemNotMappedException(serverItem);
282 return item;
285 public bool IsLocalPathMapped(string localPath)
287 foreach (WorkingFolder workingFolder in Folders)
289 if (workingFolder.LocalItem == localPath) return true;
292 return false;
295 public bool IsServerPathMapped(string serverPath)
297 foreach (WorkingFolder workingFolder in Folders)
299 if (workingFolder.ServerItem == serverPath) return true;
302 return false;
305 public void Map(string teamProject, string sourceProject) {
306 WorkingFolder[] folders = new WorkingFolder[1];
307 folders[0] = new WorkingFolder(sourceProject, teamProject);
308 Update(Name, OwnerName, folders);
311 public int PendAdd(string path)
313 return PendAdd(path, false);
316 public int PendAdd(string path, bool isRecursive)
318 string[] paths = new string[1];
319 paths[0] = path;
320 return PendAdd(paths, isRecursive);
323 public int PendAdd(string[] paths, bool isRecursive)
325 List<ChangeRequest> changes = new List<ChangeRequest>();
327 foreach (string path in paths)
329 ItemType itemType = ItemType.File;
330 if (Directory.Exists(path)) itemType = ItemType.Folder;
331 changes.Add(new ChangeRequest(path, RequestType.Add, itemType));
333 if (!isRecursive || itemType != ItemType.Folder) continue;
335 DirectoryInfo dir = new DirectoryInfo(path);
336 FileInfo[] localFiles = dir.GetFiles("*", SearchOption.AllDirectories);
338 foreach (FileInfo file in localFiles)
339 changes.Add(new ChangeRequest(file.FullName, RequestType.Add, ItemType.File));
342 if (changes.Count == 0) return 0;
344 GetOperation[] operations = Repository.PendChanges(this, changes.ToArray());
345 return operations.Length;
348 public int PendDelete(string path)
350 return PendDelete(path, RecursionType.None);
353 public int PendDelete(string path, RecursionType recursionType)
355 string[] paths = new string[1];
356 paths[0] = path;
358 return PendDelete(paths, recursionType);
361 public int PendDelete(string[] paths, RecursionType recursionType)
363 List<ChangeRequest> changes = new List<ChangeRequest>();
364 foreach (string path in paths)
366 ItemType itemType = ItemType.File;
367 if (Directory.Exists(path)) itemType = ItemType.Folder;
368 changes.Add(new ChangeRequest(path, RequestType.Delete, itemType));
371 if (changes.Count == 0) return 0;
373 GetOperation[] operations = Repository.PendChanges(this, changes.ToArray());
374 UpdateLocalVersionQueue updates = new UpdateLocalVersionQueue(this);
376 foreach (GetOperation operation in operations)
378 if (File.Exists(operation.SourceLocalItem))
380 File.SetAttributes(operation.SourceLocalItem, FileAttributes.Normal);
381 File.Delete(operation.SourceLocalItem);
383 else
385 Directory.Delete(operation.SourceLocalItem);
388 updates.QueueUpdate(operation.ItemId, null, operation.VersionServer);
391 updates.Flush();
392 return operations.Length;
395 public int PendEdit(string path)
397 // Annotation[] annotations = Repository.QueryAnnotation("ExclusiveCheckout",
398 // path, 0);
399 // foreach (Annotation annotation in annotations)
400 // {
401 // Console.WriteLine(annotation.ToString());
402 // }
404 return PendEdit(path, RecursionType.None);
407 public int PendEdit(string path, RecursionType recursionType)
409 string[] paths = new string[1];
410 paths[0] = path;
412 return PendEdit(paths, recursionType);
415 public int PendEdit(string[] paths, RecursionType recursionType)
417 List<ChangeRequest> changes = new List<ChangeRequest>();
418 foreach (string path in paths)
420 changes.Add(new ChangeRequest(path, RequestType.Edit, ItemType.File));
423 if (changes.Count == 0) return 0;
425 GetOperation[] getOperations = Repository.PendChanges(this, changes.ToArray());
427 foreach (GetOperation getOperation in getOperations)
429 File.SetAttributes(getOperation.TargetLocalItem, FileAttributes.Normal);
432 return getOperations.Length;
435 public int PendRename(string oldPath, string newPath)
437 string newServerPath;
438 if (VersionControlPath.IsServerItem(newPath)) newServerPath = GetServerItemForLocalItem(newPath);
439 else newServerPath = newPath;
441 ItemType itemType = ItemType.File;
442 if (Directory.Exists(oldPath)) itemType = ItemType.Folder;
444 List<ChangeRequest> changes = new List<ChangeRequest>();
445 changes.Add(new ChangeRequest(oldPath, newServerPath, RequestType.Rename, itemType));
447 GetOperation[] getOperations = Repository.PendChanges(this, changes.ToArray());
449 UpdateLocalVersionQueue updates = new UpdateLocalVersionQueue(this);
450 foreach (GetOperation getOperation in getOperations)
452 File.Move(oldPath, getOperation.TargetLocalItem);
453 updates.QueueUpdate(getOperation.ItemId, getOperation.TargetLocalItem, getOperation.VersionServer);
456 updates.Flush();
457 return 1;
460 internal static Workspace FromXml(Repository repository, XmlReader reader)
462 string elementName = reader.Name;
464 string computer = reader.GetAttribute("computer");
465 string name = reader.GetAttribute("name");
466 string owner = reader.GetAttribute("owner");
468 string comment = "";
469 DateTime lastAccessDate = DateTime.Now;
470 List<WorkingFolder> folders = new List<WorkingFolder>();
472 while (reader.Read())
474 if (reader.NodeType == XmlNodeType.EndElement && reader.Name == elementName)
475 break;
477 if (reader.NodeType == XmlNodeType.Element)
479 switch (reader.Name)
481 case "WorkingFolder":
482 folders.Add(WorkingFolder.FromXml(repository, reader));
483 break;
484 case "Comment":
485 comment = reader.ReadString();
486 break;
487 case "LastAccessDate":
488 lastAccessDate = reader.ReadElementContentAsDateTime();
489 break;
494 Workspace w = new Workspace(repository.VersionControlServer, name, owner, comment, folders.ToArray(), computer);
495 w.lastAccessDate = lastAccessDate;
496 return w;
499 internal void ToXml(XmlWriter writer, string element)
501 writer.WriteStartElement(element);
502 writer.WriteAttributeString("computer", Computer);
503 writer.WriteAttributeString("name", Name);
504 writer.WriteAttributeString("owner", OwnerName);
505 writer.WriteElementString("Comment", Comment);
507 if (folders != null)
509 writer.WriteStartElement("Folders");
511 foreach (WorkingFolder folder in folders)
513 folder.ToXml(writer, element);
516 writer.WriteEndElement();
519 writer.WriteEndElement();
522 public override string ToString()
524 StringBuilder sb = new StringBuilder();
526 sb.Append("Workspace instance ");
527 sb.Append(GetHashCode());
529 sb.Append("\n Comment: ");
530 sb.Append(Comment);
532 sb.Append("\n LastAccessDate: ");
533 sb.Append(LastAccessDate);
535 sb.Append("\n Folders: ");
536 if (folders != null)
538 foreach (WorkingFolder folder in folders)
540 sb.Append(folder.ToString());
544 sb.Append("\n Computer: ");
545 sb.Append(Computer);
547 sb.Append("\n Name: ");
548 sb.Append(Name);
550 sb.Append("\n OwnerName: ");
551 sb.Append(OwnerName);
553 return sb.ToString();
556 public string TryGetServerItemForLocalItem(string localItem)
558 foreach (WorkingFolder folder in folders)
560 if (!localItem.StartsWith(folder.LocalItem, StringComparison.InvariantCultureIgnoreCase)) continue;
561 string suffix = localItem.Substring(folder.LocalItem.Length);
562 return String.Format("{0}{1}", folder.ServerItem, suffix);
565 return null;
568 public string TryGetLocalItemForServerItem(string serverItem)
570 foreach (WorkingFolder folder in folders)
572 if (!serverItem.StartsWith(folder.ServerItem, StringComparison.InvariantCultureIgnoreCase)) continue;
573 string suffix = serverItem.Substring(folder.ServerItem.Length);
574 return String.Format("{0}{1}", folder.LocalItem, suffix);
577 return null;
580 public WorkingFolder TryGetWorkingFolderForServerItem(string serverItem)
582 int maxPath = 0;
583 WorkingFolder workingFolder = null;
585 foreach (WorkingFolder folder in folders)
587 if (!serverItem.StartsWith(folder.ServerItem, StringComparison.InvariantCultureIgnoreCase)) continue;
589 if (folder.LocalItem.Length > maxPath)
591 workingFolder = folder;
592 maxPath = folder.LocalItem.Length;
596 return workingFolder;
599 public int Undo(string path)
601 return Undo(path, RecursionType.None);
604 public int Undo(string path, RecursionType recursionType)
606 string[] paths = new string[1];
607 paths[0] = path;
608 return Undo(paths, recursionType);
611 public int Undo(string[] paths, RecursionType recursionType)
613 List<ItemSpec> specs = new List<ItemSpec>();
615 foreach (string path in paths)
617 specs.Add(new ItemSpec(path, recursionType));
620 // is this the same logic as a workspace Get?
621 // can we make one function to handle both cases?
623 GetOperation[] getOperations = Repository.UndoPendingChanges(Name, OwnerName, specs.ToArray());
624 foreach (GetOperation getOperation in getOperations)
626 if (getOperation.ChangeType == ChangeType.Edit ||
627 getOperation.ChangeType == ChangeType.Delete)
629 string uPath = getOperation.TargetLocalItem;
630 string directory = uPath;
632 if (getOperation.ItemType == ItemType.File)
633 directory = Path.GetDirectoryName(uPath);
635 if (!Directory.Exists(directory))
636 Directory.CreateDirectory(directory);
638 if (getOperation.ItemType == ItemType.File)
639 DownloadFile.WriteTo(uPath, Repository, getOperation.ArtifactUri);
643 return getOperations.Length;
646 public void Update (string newName, string newComment, WorkingFolder[] newMappings)
648 Workspace w1 = new Workspace(VersionControlServer, newName, OwnerName,
649 newComment, newMappings, Computer);
650 Workspace w2 = Repository.UpdateWorkspace(Name, OwnerName, w1);
652 Workstation.Current.UpdateCachedWorkspaceInfo(VersionControlServer.Uri,
653 w2, newMappings);
655 folders = w2.Folders;
658 public void RefreshMappings ()
660 Workspace w = Repository.QueryWorkspace(Name, OwnerName);
661 this.folders = w.folders;
664 public string Comment
666 get { return comment; }
669 public string Computer
671 get { return computer; }
674 public WorkingFolder[] Folders
676 get { return folders; }
679 public string Name
681 get { return name; }
684 public DateTime LastAccessDate
686 get { return lastAccessDate; }
689 public string OwnerName
691 get { return ownerName; }
694 public VersionControlServer VersionControlServer
696 get { return versionControlServer; }
699 internal Repository Repository
701 get { return versionControlServer.Repository; }