folder.icon
[tfs.git] / class / Microsoft.TeamFoundation.VersionControl.Client / Workspace.cs
blob10eadbc6a1a5c4bdf879efa6d9f42470db294f92
1 //
2 // Microsoft.TeamFoundation.VersionControl.Client.Workspace
3 //
4 // Authors:
5 // Joel Reed (joelwreed@gmail.com)
6 //
7 // Copyright (C) 2007 Joel Reed
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;
49 internal bool readWriteSetting;
51 internal Workspace(VersionControlServer versionControlServer, string name,
52 string ownerName, string comment,
53 WorkingFolder[] folders, string computer)
55 this.versionControlServer = versionControlServer;
56 this.name = name;
57 this.ownerName = ownerName;
58 this.comment = comment;
59 this.folders = folders;
60 this.computer = computer;
62 readWriteSetting = Workstation.Settings["File.ReadWrite"];
65 internal Workspace(VersionControlServer versionControlServer,
66 WorkspaceInfo info)
68 this.versionControlServer = versionControlServer;
69 this.name = info.Name;
70 this.ownerName = info.OwnerName;
71 this.comment = info.Comment;
72 this.folders = new WorkingFolder[0];
73 this.computer = info.Computer;
75 readWriteSetting = Workstation.Settings["File.ReadWrite"];
78 public int CheckIn(PendingChange[] changes, string comment)
80 if (changes.Length == 0) return 0;
82 List<string> serverItems = new List<string>();
83 SortedList<string, PendingChange> changesByServerPath = new SortedList<string, PendingChange>();
85 foreach (PendingChange change in changes)
87 // upload new or changed files only
88 if ((change.ItemType == ItemType.File) &&
89 (change.IsAdd || change.IsEdit ))
91 Repository.CheckInFile(Name, OwnerName, change);
92 SetFileAttributes(change.LocalItem);
95 serverItems.Add(change.ServerItem);
96 changesByServerPath.Add(change.ServerItem, change);
99 SortedList<string, bool> undoneServerItems = new SortedList<string, bool>();
100 int cset = Repository.CheckIn(this, serverItems.ToArray(), comment, ref undoneServerItems);
102 foreach (string undoneItem in undoneServerItems.Keys)
104 PendingChange change = changesByServerPath[undoneItem];
105 VersionControlServer.OnUndonePendingChange(this, change);
108 return cset;
111 public int CompareTo (object o)
113 Workspace a = this, b = (Workspace)o;
115 int r0 = a.VersionControlServer.ServerGuid.CompareTo (b.VersionControlServer.ServerGuid);
116 if (r0 != 0) return r0;
118 int r1 = a.Name.CompareTo (b.Name);
119 if (r1 != 0) return r1;
121 int r2 = a.OwnerName.CompareTo (b.OwnerName);
122 return r2;
125 public PendingChange[] GetPendingChanges()
127 return GetPendingChanges(VersionControlPath.RootFolder, RecursionType.Full);
130 public PendingChange[] GetPendingChanges(string item)
132 return GetPendingChanges(item, RecursionType.None);
135 public PendingChange[] GetPendingChanges(string item, RecursionType rtype)
137 return GetPendingChanges(item, rtype, false);
140 public PendingChange[] GetPendingChanges(string item, RecursionType rtype,
141 bool includeDownloadInfo)
143 string[] items = new string[1];
144 items[0] = item;
145 return GetPendingChanges(items, rtype, includeDownloadInfo);
148 public PendingChange[] GetPendingChanges(string[] items, RecursionType rtype)
150 return GetPendingChanges(items, rtype, false);
153 public PendingChange[] GetPendingChanges(string[] items, RecursionType rtype,
154 bool includeDownloadInfo)
156 List<ItemSpec> itemSpecs = new List<ItemSpec>();
157 foreach (string item in items)
159 itemSpecs.Add(new ItemSpec(item, rtype));
162 Failure[] failures = null;
163 PendingChange[] changes = Repository.QueryPendingSets(Name, OwnerName, Name, OwnerName,
164 itemSpecs.ToArray(), includeDownloadInfo,
165 out failures);
166 foreach (Failure failure in failures)
168 Console.WriteLine(failure.ToString());
171 return changes;
174 public void Delete()
176 Repository.DeleteWorkspace(Name, OwnerName);
177 Workstation.Current.RemoveCachedWorkspaceInfo(VersionControlServer.Uri, Name);
180 public GetStatus Get()
182 return Get(VersionSpec.Latest, GetOptions.None);
185 public GetStatus Get(VersionSpec versionSpec, GetOptions options)
187 GetRequest request = new GetRequest(versionSpec);
188 return Get(request, GetOptions.None, null, null);
191 public GetStatus Get(GetRequest request, GetOptions options)
193 return Get(request, options, null, null);
196 public GetStatus Get(GetRequest[] requests, GetOptions options)
198 return Get(requests, options, null, null);
201 public GetStatus Get(GetRequest request, GetOptions options,
202 GetFilterCallback filterCallback, object userData)
204 GetRequest[] requests = new GetRequest[1];
205 requests[0] = request;
206 return Get(requests, options, filterCallback, userData);
209 public GetStatus Get (string[] items, VersionSpec version,
210 RecursionType recursion, GetOptions options)
212 List<GetRequest> requests = new List<GetRequest>();
213 foreach (string item in items)
215 requests.Add(new GetRequest(item, recursion, version));
218 return Get(requests.ToArray(), options, null, null);
221 public GetStatus Get(GetRequest[] requests, GetOptions options,
222 GetFilterCallback filterCallback, object userData)
224 bool force = ((GetOptions.Overwrite & options) == GetOptions.Overwrite);
225 bool noGet = false; // not implemented below: ((GetOptions.Preview & options) == GetOptions.Preview);
227 GetOperation[] getOperations = Repository.Get(Name, OwnerName, requests, force, noGet);
228 if (null != filterCallback) filterCallback(this, getOperations, userData);
230 UpdateLocalVersionQueue updates = new UpdateLocalVersionQueue(this);
231 foreach (GetOperation getOperation in getOperations)
233 string uPath = null;
234 GettingEventArgs args = new GettingEventArgs(this, getOperation);
236 // Console.WriteLine(getOperation.ToString());
238 if (getOperation.DeletionId != 0)
240 if ((getOperation.ItemType == ItemType.Folder)&&
241 (Directory.Exists(getOperation.SourceLocalItem)))
243 UnsetDirectoryAttributes(getOperation.SourceLocalItem);
244 Directory.Delete(getOperation.SourceLocalItem, true);
246 else if ((getOperation.ItemType == ItemType.File)&&
247 (File.Exists(getOperation.SourceLocalItem)))
249 UnsetFileAttributes(getOperation.SourceLocalItem);
250 File.Delete(getOperation.SourceLocalItem);
253 else if ((!String.IsNullOrEmpty(getOperation.TargetLocalItem))&&
254 (!String.IsNullOrEmpty(getOperation.SourceLocalItem))&&
255 (getOperation.SourceLocalItem != getOperation.TargetLocalItem))
257 uPath = getOperation.TargetLocalItem;
260 File.Move(getOperation.SourceLocalItem, getOperation.TargetLocalItem);
262 catch (IOException)
264 args.Status = OperationStatus.TargetIsDirectory;
267 else if (getOperation.ChangeType == ChangeType.None &&
268 getOperation.VersionServer != 0)
270 uPath = getOperation.TargetLocalItem;
271 string directory = uPath;
273 if (getOperation.ItemType == ItemType.File)
274 directory = Path.GetDirectoryName(uPath);
276 if (!Directory.Exists(directory))
277 Directory.CreateDirectory(directory);
279 if (getOperation.ItemType == ItemType.File)
281 DownloadFile.WriteTo(uPath, Repository, getOperation.ArtifactUri);
282 SetFileAttributes(uPath);
286 versionControlServer.OnDownloading(args);
287 updates.QueueUpdate(getOperation.ItemId, uPath, getOperation.VersionServer);
290 updates.Flush();
291 return new GetStatus(getOperations.Length);
294 public ExtendedItem[][] GetExtendedItems (ItemSpec[] itemSpecs,
295 DeletedState deletedState,
296 ItemType itemType)
298 return Repository.QueryItemsExtended(Name, OwnerName,
299 itemSpecs, deletedState, itemType);
302 public string GetServerItemForLocalItem(string localItem)
304 string item = TryGetServerItemForLocalItem(localItem);
305 if (item == null)
306 throw new ItemNotMappedException(localItem);
307 return item;
310 public string GetLocalItemForServerItem(string serverItem)
312 string item = TryGetLocalItemForServerItem(serverItem);
313 if (item == null)
314 throw new ItemNotMappedException(serverItem);
315 return item;
318 public bool IsLocalPathMapped(string localPath)
320 foreach (WorkingFolder workingFolder in Folders)
322 if (localPath.StartsWith(workingFolder.LocalItem))
323 return true;
326 return false;
329 public bool IsServerPathMapped(string serverPath)
331 foreach (WorkingFolder workingFolder in Folders)
333 if (serverPath.StartsWith(workingFolder.ServerItem))
334 return true;
337 return false;
340 public void Map(string teamProject, string sourceProject) {
341 WorkingFolder[] folders = new WorkingFolder[1];
342 folders[0] = new WorkingFolder(sourceProject, teamProject);
343 Update(Name, OwnerName, folders);
346 public int PendAdd(string path)
348 return PendAdd(path, false);
351 public int PendAdd(string path, bool isRecursive)
353 string[] paths = new string[1];
354 paths[0] = path;
355 return PendAdd(paths, isRecursive);
358 public int PendAdd(string[] paths, bool isRecursive)
360 List<ChangeRequest> changes = new List<ChangeRequest>();
362 foreach (string path in paths)
364 ItemType itemType = ItemType.File;
365 if (Directory.Exists(path)) itemType = ItemType.Folder;
366 changes.Add(new ChangeRequest(path, RequestType.Add, itemType));
368 if (!isRecursive || itemType != ItemType.Folder) continue;
370 DirectoryInfo dir = new DirectoryInfo(path);
371 FileInfo[] localFiles = dir.GetFiles("*", SearchOption.AllDirectories);
373 foreach (FileInfo file in localFiles)
374 changes.Add(new ChangeRequest(file.FullName, RequestType.Add, ItemType.File));
377 if (changes.Count == 0) return 0;
379 GetOperation[] operations = Repository.PendChanges(this, changes.ToArray());
380 return operations.Length;
383 public int PendDelete(string path)
385 return PendDelete(path, RecursionType.None);
388 public int PendDelete(string path, RecursionType recursionType)
390 string[] paths = new string[1];
391 paths[0] = path;
393 return PendDelete(paths, recursionType);
396 public int PendDelete(string[] paths, RecursionType recursionType)
398 List<ChangeRequest> changes = new List<ChangeRequest>();
399 foreach (string path in paths)
401 ItemType itemType = ItemType.File;
402 if (Directory.Exists(path)) itemType = ItemType.Folder;
403 changes.Add(new ChangeRequest(path, RequestType.Delete, itemType));
406 if (changes.Count == 0) return 0;
408 GetOperation[] operations = Repository.PendChanges(this, changes.ToArray());
409 UpdateLocalVersionQueue updates = new UpdateLocalVersionQueue(this);
411 // first delete all files
412 foreach (GetOperation operation in operations)
414 if (operation.ItemType != ItemType.File) continue;
415 if (!File.Exists(operation.SourceLocalItem)) continue;
417 UnsetFileAttributes(operation.SourceLocalItem);
418 File.Delete(operation.SourceLocalItem);
419 updates.QueueUpdate(operation.ItemId, null, operation.VersionServer);
422 // then any directories
423 foreach (GetOperation operation in operations)
425 if (operation.ItemType != ItemType.Folder) continue;
426 if (!Directory.Exists(operation.SourceLocalItem)) continue;
428 //DirectoryInfo dir = new DirectoryInfo(operation.SourceLocalItem);
429 //FileInfo[] localFiles = dir.GetFiles("*", SearchOption.AllDirectories);
430 //foreach (FileInfo file in localFiles)
431 // UnsetFileAttributes(file.FullName);
433 Directory.Delete(operation.SourceLocalItem, true);
434 updates.QueueUpdate(operation.ItemId, null, operation.VersionServer);
437 updates.Flush();
438 return operations.Length;
441 public int PendEdit(string path)
443 // Annotation[] annotations = Repository.QueryAnnotation("ExclusiveCheckout",
444 // path, 0);
445 // foreach (Annotation annotation in annotations)
446 // {
447 // Console.WriteLine(annotation.ToString());
448 // }
450 return PendEdit(path, RecursionType.None);
453 public int PendEdit(string path, RecursionType recursionType)
455 string[] paths = new string[1];
456 paths[0] = path;
458 return PendEdit(paths, recursionType);
461 public int PendEdit(string[] paths, RecursionType recursionType)
463 List<ChangeRequest> changes = new List<ChangeRequest>();
464 foreach (string path in paths)
466 changes.Add(new ChangeRequest(path, RequestType.Edit, ItemType.File));
469 if (changes.Count == 0) return 0;
471 GetOperation[] getOperations = Repository.PendChanges(this, changes.ToArray());
472 foreach (GetOperation getOperation in getOperations)
474 UnsetFileAttributes(getOperation.TargetLocalItem);
477 return getOperations.Length;
480 public int PendRename(string oldPath, string newPath)
482 string newServerPath;
483 if (VersionControlPath.IsServerItem(newPath)) newServerPath = GetServerItemForLocalItem(newPath);
484 else newServerPath = newPath;
486 ItemType itemType = ItemType.File;
487 if (Directory.Exists(oldPath)) itemType = ItemType.Folder;
489 List<ChangeRequest> changes = new List<ChangeRequest>();
490 changes.Add(new ChangeRequest(oldPath, newServerPath, RequestType.Rename, itemType));
492 GetOperation[] getOperations = Repository.PendChanges(this, changes.ToArray());
494 UpdateLocalVersionQueue updates = new UpdateLocalVersionQueue(this);
495 foreach (GetOperation getOperation in getOperations)
497 if (itemType == ItemType.File)
498 File.Move(oldPath, getOperation.TargetLocalItem);
499 else
500 Directory.Move(oldPath, getOperation.TargetLocalItem);
502 updates.QueueUpdate(getOperation.ItemId, getOperation.TargetLocalItem, getOperation.VersionServer);
505 updates.Flush();
506 return 1;
509 public int SetLock(string path, LockLevel lockLevel)
511 return SetLock(path, lockLevel, RecursionType.None);
514 public int SetLock(string path, LockLevel lockLevel, RecursionType recursion)
516 string[] paths = new string[1];
517 paths[0] = path;
518 return SetLock(paths, lockLevel, recursion);
521 public int SetLock(string[] paths, LockLevel lockLevel)
523 return SetLock(paths, lockLevel, RecursionType.None);
526 public int SetLock(string[] paths, LockLevel lockLevel, RecursionType recursion)
528 List<ChangeRequest> changes = new List<ChangeRequest>();
530 foreach (string path in paths)
532 ItemType itemType = ItemType.File;
533 if (Directory.Exists(path)) itemType = ItemType.Folder;
534 changes.Add(new ChangeRequest(path, RequestType.Lock, itemType, recursion, lockLevel));
537 if (changes.Count == 0) return 0;
539 GetOperation[] operations = Repository.PendChanges(this, changes.ToArray());
540 return operations.Length;
543 public void Shelve (Shelveset shelveset, PendingChange[] changes,
544 ShelvingOptions options)
546 List<string> serverItems = new List<string>();
548 foreach (PendingChange change in changes)
550 // upload new or changed files only
551 if ((change.ItemType == ItemType.File) &&
552 (change.IsAdd || change.IsEdit ))
554 Repository.ShelveFile(Name, OwnerName, change);
557 serverItems.Add(change.ServerItem);
560 Repository.Shelve(this, shelveset, serverItems.ToArray(), options);
563 internal static Workspace FromXml(Repository repository, XmlReader reader)
565 string elementName = reader.Name;
567 string computer = reader.GetAttribute("computer");
568 string name = reader.GetAttribute("name");
569 string owner = reader.GetAttribute("owner");
571 string comment = "";
572 DateTime lastAccessDate = DateTime.Now;
573 List<WorkingFolder> folders = new List<WorkingFolder>();
575 while (reader.Read())
577 if (reader.NodeType == XmlNodeType.EndElement && reader.Name == elementName)
578 break;
580 if (reader.NodeType == XmlNodeType.Element)
582 switch (reader.Name)
584 case "WorkingFolder":
585 folders.Add(WorkingFolder.FromXml(repository, reader));
586 break;
587 case "Comment":
588 comment = reader.ReadString();
589 break;
590 case "LastAccessDate":
591 lastAccessDate = reader.ReadElementContentAsDateTime();
592 break;
597 Workspace w = new Workspace(repository.VersionControlServer, name, owner, comment, folders.ToArray(), computer);
598 w.lastAccessDate = lastAccessDate;
599 return w;
602 internal void ToXml(XmlWriter writer, string element)
604 writer.WriteStartElement(element);
605 writer.WriteAttributeString("computer", Computer);
606 writer.WriteAttributeString("name", Name);
607 writer.WriteAttributeString("owner", OwnerName);
608 writer.WriteElementString("Comment", Comment);
610 if (folders != null)
612 writer.WriteStartElement("Folders");
614 foreach (WorkingFolder folder in folders)
616 folder.ToXml(writer, element);
619 writer.WriteEndElement();
622 writer.WriteEndElement();
625 public override string ToString()
627 StringBuilder sb = new StringBuilder();
629 sb.Append("Workspace instance ");
630 sb.Append(GetHashCode());
632 sb.Append("\n Comment: ");
633 sb.Append(Comment);
635 sb.Append("\n LastAccessDate: ");
636 sb.Append(LastAccessDate);
638 sb.Append("\n Folders: ");
639 if (folders != null)
641 foreach (WorkingFolder folder in folders)
643 sb.Append(folder.ToString());
647 sb.Append("\n Computer: ");
648 sb.Append(Computer);
650 sb.Append("\n Name: ");
651 sb.Append(Name);
653 sb.Append("\n OwnerName: ");
654 sb.Append(OwnerName);
656 return sb.ToString();
659 public string TryGetServerItemForLocalItem(string localItem)
661 string serverItem = null;
662 int longest = 0;
664 // find the longest matching serveritem
665 foreach (WorkingFolder folder in folders)
667 //Console.WriteLine("item: {0} =? folder: {1}", localItem, folder.LocalItem);
668 if (!localItem.StartsWith(folder.LocalItem, StringComparison.InvariantCultureIgnoreCase)) continue;
670 int clen = folder.LocalItem.Length;
671 if (clen > longest)
673 serverItem = String.Format("{0}{1}", folder.ServerItem, localItem.Substring(clen));
674 longest = clen;
678 return serverItem;
681 public string TryGetLocalItemForServerItem(string serverItem)
683 string localItem = null;
684 int longest = 0;
686 foreach (WorkingFolder folder in folders)
688 if (!serverItem.StartsWith(folder.ServerItem, StringComparison.InvariantCultureIgnoreCase)) continue;
690 int clen = folder.ServerItem.Length;
691 if (clen > longest)
693 localItem = String.Format("{0}{1}", folder.LocalItem, serverItem.Substring(clen));
694 longest = clen;
698 return localItem;
701 public WorkingFolder TryGetWorkingFolderForServerItem(string serverItem)
703 int maxPath = 0;
704 WorkingFolder workingFolder = null;
706 foreach (WorkingFolder folder in folders)
708 if (!serverItem.StartsWith(folder.ServerItem, StringComparison.InvariantCultureIgnoreCase)) continue;
710 if (folder.LocalItem.Length > maxPath)
712 workingFolder = folder;
713 maxPath = folder.LocalItem.Length;
717 return workingFolder;
720 public int Undo(string path)
722 return Undo(path, RecursionType.None);
725 public int Undo(string path, RecursionType recursionType)
727 string[] paths = new string[1];
728 paths[0] = path;
729 return Undo(paths, recursionType);
732 public int Undo(string[] paths, RecursionType recursionType)
734 List<ItemSpec> specs = new List<ItemSpec>();
736 foreach (string path in paths)
738 specs.Add(new ItemSpec(path, recursionType));
741 // is this the same logic as a workspace Get?
742 // can we make one function to handle both cases?
744 GetOperation[] getOperations = Repository.UndoPendingChanges(Name, OwnerName, specs.ToArray());
745 foreach (GetOperation getOperation in getOperations)
747 if (getOperation.ChangeType == ChangeType.Edit ||
748 getOperation.ChangeType == ChangeType.Delete)
750 string uPath = getOperation.TargetLocalItem;
751 string directory = uPath;
753 if (getOperation.ItemType == ItemType.File)
754 directory = Path.GetDirectoryName(uPath);
756 if (!Directory.Exists(directory))
757 Directory.CreateDirectory(directory);
759 if (getOperation.ItemType == ItemType.File)
761 DownloadFile.WriteTo(uPath, Repository, getOperation.ArtifactUri);
762 SetFileAttributes(uPath);
767 return getOperations.Length;
770 public void Update (string newName, string newComment, WorkingFolder[] newMappings)
772 Workspace w1 = new Workspace(VersionControlServer, newName, OwnerName,
773 newComment, newMappings, Computer);
774 Workspace w2 = Repository.UpdateWorkspace(Name, OwnerName, w1);
776 Workstation.Current.UpdateCachedWorkspaceInfo(VersionControlServer.Uri,
777 w2, newMappings);
779 folders = w2.Folders;
782 public void RefreshMappings ()
784 Workspace w = Repository.QueryWorkspace(Name, OwnerName);
785 this.folders = w.folders;
788 public string Comment
790 get { return comment; }
793 public string Computer
795 get { return computer; }
798 public WorkingFolder[] Folders
800 get { return folders; }
803 public string Name
805 get { return name; }
808 public DateTime LastAccessDate
810 get { return lastAccessDate; }
813 public string OwnerName
815 get { return ownerName; }
818 public VersionControlServer VersionControlServer
820 get { return versionControlServer; }
823 internal Repository Repository
825 get { return versionControlServer.Repository; }
828 internal void SetFileAttributes(string path)
830 if (!readWriteSetting)
831 File.SetAttributes(path, FileAttributes.ReadOnly);
834 internal void UnsetFileAttributes(string path)
836 if (!readWriteSetting)
837 File.SetAttributes(path, FileAttributes.Normal);
840 internal void UnsetDirectoryAttributes(string path)
842 if (readWriteSetting) return;
844 DirectoryInfo dir = new DirectoryInfo(path);
845 FileInfo[] localFiles = dir.GetFiles("*", SearchOption.AllDirectories);
846 foreach (FileInfo file in localFiles)
847 File.SetAttributes(file.FullName, FileAttributes.Normal);