2 // Microsoft.TeamFoundation.VersionControl.Client.Workspace
5 // Joel Reed (joelwreed@gmail.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System
.Collections
.Generic
;
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
;
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
;
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
,
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 foreach (PendingChange change
in changes
)
85 // upload new or changed files only
86 if ((change
.ItemType
== ItemType
.File
) &&
87 (change
.IsAdd
|| change
.IsEdit
))
89 Repository
.UploadFile(Name
, OwnerName
, change
);
90 SetFileAttributes(change
.LocalItem
);
93 serverItems
.Add(change
.ServerItem
);
96 return Repository
.CheckIn(this, serverItems
.ToArray(), comment
);
99 public int CompareTo (object o
)
101 Workspace a
= this, b
= (Workspace
)o
;
103 int r0
= a
.VersionControlServer
.ServerGuid
.CompareTo (b
.VersionControlServer
.ServerGuid
);
104 if (r0
!= 0) return r0
;
106 int r1
= a
.Name
.CompareTo (b
.Name
);
107 if (r1
!= 0) return r1
;
109 int r2
= a
.OwnerName
.CompareTo (b
.OwnerName
);
113 public PendingChange
[] GetPendingChanges()
115 return GetPendingChanges(VersionControlPath
.RootFolder
, RecursionType
.Full
);
118 public PendingChange
[] GetPendingChanges(string item
)
120 return GetPendingChanges(item
, RecursionType
.None
);
123 public PendingChange
[] GetPendingChanges(string item
, RecursionType rtype
)
125 return GetPendingChanges(item
, rtype
, false);
128 public PendingChange
[] GetPendingChanges(string item
, RecursionType rtype
,
129 bool includeDownloadInfo
)
131 string[] items
= new string[1];
133 return GetPendingChanges(items
, rtype
, includeDownloadInfo
);
136 public PendingChange
[] GetPendingChanges(string[] items
, RecursionType rtype
)
138 return GetPendingChanges(items
, rtype
, false);
141 public PendingChange
[] GetPendingChanges(string[] items
, RecursionType rtype
,
142 bool includeDownloadInfo
)
144 List
<ItemSpec
> itemSpecs
= new List
<ItemSpec
>();
145 foreach (string item
in items
)
147 itemSpecs
.Add(new ItemSpec(item
, rtype
));
150 Failure
[] failures
= null;
151 PendingChange
[] changes
= Repository
.QueryPendingSets(Name
, OwnerName
, Name
, OwnerName
,
152 itemSpecs
.ToArray(), includeDownloadInfo
,
154 foreach (Failure failure
in failures
)
156 Console
.WriteLine(failure
.ToString());
164 Repository
.DeleteWorkspace(Name
, OwnerName
);
165 Workstation
.Current
.RemoveCachedWorkspaceInfo(VersionControlServer
.Uri
, Name
);
168 public GetStatus
Get()
170 return Get(VersionSpec
.Latest
, GetOptions
.None
);
173 public GetStatus
Get(VersionSpec versionSpec
, GetOptions options
)
175 GetRequest request
= new GetRequest(versionSpec
);
176 return Get(request
, GetOptions
.None
, null, null);
179 public GetStatus
Get(GetRequest request
, GetOptions options
)
181 return Get(request
, options
, null, null);
184 public GetStatus
Get(GetRequest
[] requests
, GetOptions options
)
186 return Get(requests
, options
, null, null);
189 public GetStatus
Get(GetRequest request
, GetOptions options
,
190 GetFilterCallback filterCallback
, object userData
)
192 GetRequest
[] requests
= new GetRequest
[1];
193 requests
[0] = request
;
194 return Get(requests
, options
, filterCallback
, userData
);
197 public GetStatus
Get (string[] items
, VersionSpec version
,
198 RecursionType recursion
, GetOptions options
)
200 List
<GetRequest
> requests
= new List
<GetRequest
>();
201 foreach (string item
in items
)
203 requests
.Add(new GetRequest(item
, recursion
, version
));
206 return Get(requests
.ToArray(), options
, null, null);
209 public GetStatus
Get(GetRequest
[] requests
, GetOptions options
,
210 GetFilterCallback filterCallback
, object userData
)
212 bool force
= ((GetOptions
.Overwrite
& options
) == GetOptions
.Overwrite
);
213 bool noGet
= ! ((GetOptions
.GetAll
& options
) == GetOptions
.GetAll
);
215 GetOperation
[] getOperations
= Repository
.Get(Name
, OwnerName
, requests
, force
, noGet
);
216 if (null != filterCallback
) filterCallback(this, getOperations
, userData
);
218 UpdateLocalVersionQueue updates
= new UpdateLocalVersionQueue(this);
219 foreach (GetOperation getOperation
in getOperations
)
222 GettingEventArgs args
= new GettingEventArgs(this, getOperation
);
224 //Console.WriteLine(getOperation.ToString());
226 if (getOperation
.DeletionId
!= 0)
228 if ((getOperation
.ItemType
== ItemType
.Folder
)&&
229 (Directory
.Exists(getOperation
.SourceLocalItem
)))
231 Directory
.Delete(getOperation
.SourceLocalItem
, true);
233 else if ((getOperation
.ItemType
== ItemType
.File
)&&
234 (File
.Exists(getOperation
.SourceLocalItem
)))
236 UnsetFileAttributes(getOperation
.SourceLocalItem
);
237 File
.Delete(getOperation
.SourceLocalItem
);
240 else if ((!String
.IsNullOrEmpty(getOperation
.TargetLocalItem
))&&
241 (!String
.IsNullOrEmpty(getOperation
.SourceLocalItem
))&&
242 (getOperation
.SourceLocalItem
!= getOperation
.TargetLocalItem
))
244 uPath
= getOperation
.TargetLocalItem
;
247 File
.Move(getOperation
.SourceLocalItem
, getOperation
.TargetLocalItem
);
251 args
.Status
= OperationStatus
.TargetIsDirectory
;
254 else if (getOperation
.ChangeType
== ChangeType
.None
)
256 uPath
= getOperation
.TargetLocalItem
;
257 string directory
= uPath
;
259 if (getOperation
.ItemType
== ItemType
.File
)
260 directory
= Path
.GetDirectoryName(uPath
);
262 if (!Directory
.Exists(directory
))
263 Directory
.CreateDirectory(directory
);
265 if (getOperation
.ItemType
== ItemType
.File
)
267 DownloadFile
.WriteTo(uPath
, Repository
, getOperation
.ArtifactUri
);
268 SetFileAttributes(uPath
);
272 versionControlServer
.OnDownloading(args
);
273 updates
.QueueUpdate(getOperation
.ItemId
, uPath
, getOperation
.VersionServer
);
277 return new GetStatus(getOperations
.Length
);
280 public ExtendedItem
[][] GetExtendedItems (ItemSpec
[] itemSpecs
,
281 DeletedState deletedState
,
284 return Repository
.QueryItemsExtended(Name
, OwnerName
,
285 itemSpecs
, deletedState
, itemType
);
288 public string GetServerItemForLocalItem(string localItem
)
290 string item
= TryGetServerItemForLocalItem(localItem
);
292 throw new ItemNotMappedException(localItem
);
296 public string GetLocalItemForServerItem(string serverItem
)
298 string item
= TryGetLocalItemForServerItem(serverItem
);
300 throw new ItemNotMappedException(serverItem
);
304 public bool IsLocalPathMapped(string localPath
)
306 foreach (WorkingFolder workingFolder
in Folders
)
308 if (workingFolder
.LocalItem
== localPath
) return true;
314 public bool IsServerPathMapped(string serverPath
)
316 foreach (WorkingFolder workingFolder
in Folders
)
318 if (workingFolder
.ServerItem
== serverPath
) return true;
324 public void Map(string teamProject
, string sourceProject
) {
325 WorkingFolder
[] folders
= new WorkingFolder
[1];
326 folders
[0] = new WorkingFolder(sourceProject
, teamProject
);
327 Update(Name
, OwnerName
, folders
);
330 public int PendAdd(string path
)
332 return PendAdd(path
, false);
335 public int PendAdd(string path
, bool isRecursive
)
337 string[] paths
= new string[1];
339 return PendAdd(paths
, isRecursive
);
342 public int PendAdd(string[] paths
, bool isRecursive
)
344 List
<ChangeRequest
> changes
= new List
<ChangeRequest
>();
346 foreach (string path
in paths
)
348 ItemType itemType
= ItemType
.File
;
349 if (Directory
.Exists(path
)) itemType
= ItemType
.Folder
;
350 changes
.Add(new ChangeRequest(path
, RequestType
.Add
, itemType
));
352 if (!isRecursive
|| itemType
!= ItemType
.Folder
) continue;
354 DirectoryInfo dir
= new DirectoryInfo(path
);
355 FileInfo
[] localFiles
= dir
.GetFiles("*", SearchOption
.AllDirectories
);
357 foreach (FileInfo file
in localFiles
)
358 changes
.Add(new ChangeRequest(file
.FullName
, RequestType
.Add
, ItemType
.File
));
361 if (changes
.Count
== 0) return 0;
363 GetOperation
[] operations
= Repository
.PendChanges(this, changes
.ToArray());
364 return operations
.Length
;
367 public int PendDelete(string path
)
369 return PendDelete(path
, RecursionType
.None
);
372 public int PendDelete(string path
, RecursionType recursionType
)
374 string[] paths
= new string[1];
377 return PendDelete(paths
, recursionType
);
380 public int PendDelete(string[] paths
, RecursionType recursionType
)
382 List
<ChangeRequest
> changes
= new List
<ChangeRequest
>();
383 foreach (string path
in paths
)
385 ItemType itemType
= ItemType
.File
;
386 if (Directory
.Exists(path
)) itemType
= ItemType
.Folder
;
387 changes
.Add(new ChangeRequest(path
, RequestType
.Delete
, itemType
));
390 if (changes
.Count
== 0) return 0;
392 GetOperation
[] operations
= Repository
.PendChanges(this, changes
.ToArray());
393 UpdateLocalVersionQueue updates
= new UpdateLocalVersionQueue(this);
395 foreach (GetOperation operation
in operations
)
397 if (File
.Exists(operation
.SourceLocalItem
))
399 UnsetFileAttributes(operation
.SourceLocalItem
);
400 File
.Delete(operation
.SourceLocalItem
);
404 DirectoryInfo dir
= new DirectoryInfo(operation
.SourceLocalItem
);
405 FileInfo
[] localFiles
= dir
.GetFiles("*", SearchOption
.AllDirectories
);
406 foreach (FileInfo file
in localFiles
)
407 UnsetFileAttributes(file
.FullName
);
409 Directory
.Delete(operation
.SourceLocalItem
, true);
412 updates
.QueueUpdate(operation
.ItemId
, null, operation
.VersionServer
);
416 return operations
.Length
;
419 public int PendEdit(string path
)
421 // Annotation[] annotations = Repository.QueryAnnotation("ExclusiveCheckout",
423 // foreach (Annotation annotation in annotations)
425 // Console.WriteLine(annotation.ToString());
428 return PendEdit(path
, RecursionType
.None
);
431 public int PendEdit(string path
, RecursionType recursionType
)
433 string[] paths
= new string[1];
436 return PendEdit(paths
, recursionType
);
439 public int PendEdit(string[] paths
, RecursionType recursionType
)
441 List
<ChangeRequest
> changes
= new List
<ChangeRequest
>();
442 foreach (string path
in paths
)
444 changes
.Add(new ChangeRequest(path
, RequestType
.Edit
, ItemType
.File
));
447 if (changes
.Count
== 0) return 0;
449 GetOperation
[] getOperations
= Repository
.PendChanges(this, changes
.ToArray());
450 foreach (GetOperation getOperation
in getOperations
)
452 UnsetFileAttributes(getOperation
.TargetLocalItem
);
455 return getOperations
.Length
;
458 public int PendRename(string oldPath
, string newPath
)
460 string newServerPath
;
461 if (VersionControlPath
.IsServerItem(newPath
)) newServerPath
= GetServerItemForLocalItem(newPath
);
462 else newServerPath
= newPath
;
464 ItemType itemType
= ItemType
.File
;
465 if (Directory
.Exists(oldPath
)) itemType
= ItemType
.Folder
;
467 List
<ChangeRequest
> changes
= new List
<ChangeRequest
>();
468 changes
.Add(new ChangeRequest(oldPath
, newServerPath
, RequestType
.Rename
, itemType
));
470 GetOperation
[] getOperations
= Repository
.PendChanges(this, changes
.ToArray());
472 UpdateLocalVersionQueue updates
= new UpdateLocalVersionQueue(this);
473 foreach (GetOperation getOperation
in getOperations
)
475 File
.Move(oldPath
, getOperation
.TargetLocalItem
);
476 updates
.QueueUpdate(getOperation
.ItemId
, getOperation
.TargetLocalItem
, getOperation
.VersionServer
);
483 internal static Workspace
FromXml(Repository repository
, XmlReader reader
)
485 string elementName
= reader
.Name
;
487 string computer
= reader
.GetAttribute("computer");
488 string name
= reader
.GetAttribute("name");
489 string owner
= reader
.GetAttribute("owner");
492 DateTime lastAccessDate
= DateTime
.Now
;
493 List
<WorkingFolder
> folders
= new List
<WorkingFolder
>();
495 while (reader
.Read())
497 if (reader
.NodeType
== XmlNodeType
.EndElement
&& reader
.Name
== elementName
)
500 if (reader
.NodeType
== XmlNodeType
.Element
)
504 case "WorkingFolder":
505 folders
.Add(WorkingFolder
.FromXml(repository
, reader
));
508 comment
= reader
.ReadString();
510 case "LastAccessDate":
511 lastAccessDate
= reader
.ReadElementContentAsDateTime();
517 Workspace w
= new Workspace(repository
.VersionControlServer
, name
, owner
, comment
, folders
.ToArray(), computer
);
518 w
.lastAccessDate
= lastAccessDate
;
522 internal void ToXml(XmlWriter writer
, string element
)
524 writer
.WriteStartElement(element
);
525 writer
.WriteAttributeString("computer", Computer
);
526 writer
.WriteAttributeString("name", Name
);
527 writer
.WriteAttributeString("owner", OwnerName
);
528 writer
.WriteElementString("Comment", Comment
);
532 writer
.WriteStartElement("Folders");
534 foreach (WorkingFolder folder
in folders
)
536 folder
.ToXml(writer
, element
);
539 writer
.WriteEndElement();
542 writer
.WriteEndElement();
545 public override string ToString()
547 StringBuilder sb
= new StringBuilder();
549 sb
.Append("Workspace instance ");
550 sb
.Append(GetHashCode());
552 sb
.Append("\n Comment: ");
555 sb
.Append("\n LastAccessDate: ");
556 sb
.Append(LastAccessDate
);
558 sb
.Append("\n Folders: ");
561 foreach (WorkingFolder folder
in folders
)
563 sb
.Append(folder
.ToString());
567 sb
.Append("\n Computer: ");
570 sb
.Append("\n Name: ");
573 sb
.Append("\n OwnerName: ");
574 sb
.Append(OwnerName
);
576 return sb
.ToString();
579 public string TryGetServerItemForLocalItem(string localItem
)
581 foreach (WorkingFolder folder
in folders
)
583 if (!localItem
.StartsWith(folder
.LocalItem
, StringComparison
.InvariantCultureIgnoreCase
)) continue;
584 string suffix
= localItem
.Substring(folder
.LocalItem
.Length
);
585 return String
.Format("{0}{1}", folder
.ServerItem
, suffix
);
591 public string TryGetLocalItemForServerItem(string serverItem
)
593 foreach (WorkingFolder folder
in folders
)
595 if (!serverItem
.StartsWith(folder
.ServerItem
, StringComparison
.InvariantCultureIgnoreCase
)) continue;
596 string suffix
= serverItem
.Substring(folder
.ServerItem
.Length
);
597 return String
.Format("{0}{1}", folder
.LocalItem
, suffix
);
603 public WorkingFolder
TryGetWorkingFolderForServerItem(string serverItem
)
606 WorkingFolder workingFolder
= null;
608 foreach (WorkingFolder folder
in folders
)
610 if (!serverItem
.StartsWith(folder
.ServerItem
, StringComparison
.InvariantCultureIgnoreCase
)) continue;
612 if (folder
.LocalItem
.Length
> maxPath
)
614 workingFolder
= folder
;
615 maxPath
= folder
.LocalItem
.Length
;
619 return workingFolder
;
622 public int Undo(string path
)
624 return Undo(path
, RecursionType
.None
);
627 public int Undo(string path
, RecursionType recursionType
)
629 string[] paths
= new string[1];
631 return Undo(paths
, recursionType
);
634 public int Undo(string[] paths
, RecursionType recursionType
)
636 List
<ItemSpec
> specs
= new List
<ItemSpec
>();
638 foreach (string path
in paths
)
640 specs
.Add(new ItemSpec(path
, recursionType
));
643 // is this the same logic as a workspace Get?
644 // can we make one function to handle both cases?
646 GetOperation
[] getOperations
= Repository
.UndoPendingChanges(Name
, OwnerName
, specs
.ToArray());
647 foreach (GetOperation getOperation
in getOperations
)
649 if (getOperation
.ChangeType
== ChangeType
.Edit
||
650 getOperation
.ChangeType
== ChangeType
.Delete
)
652 string uPath
= getOperation
.TargetLocalItem
;
653 string directory
= uPath
;
655 if (getOperation
.ItemType
== ItemType
.File
)
656 directory
= Path
.GetDirectoryName(uPath
);
658 if (!Directory
.Exists(directory
))
659 Directory
.CreateDirectory(directory
);
661 if (getOperation
.ItemType
== ItemType
.File
)
663 DownloadFile
.WriteTo(uPath
, Repository
, getOperation
.ArtifactUri
);
664 SetFileAttributes(uPath
);
669 return getOperations
.Length
;
672 public void Update (string newName
, string newComment
, WorkingFolder
[] newMappings
)
674 Workspace w1
= new Workspace(VersionControlServer
, newName
, OwnerName
,
675 newComment
, newMappings
, Computer
);
676 Workspace w2
= Repository
.UpdateWorkspace(Name
, OwnerName
, w1
);
678 Workstation
.Current
.UpdateCachedWorkspaceInfo(VersionControlServer
.Uri
,
681 folders
= w2
.Folders
;
684 public void RefreshMappings ()
686 Workspace w
= Repository
.QueryWorkspace(Name
, OwnerName
);
687 this.folders
= w
.folders
;
690 public string Comment
692 get { return comment; }
695 public string Computer
697 get { return computer; }
700 public WorkingFolder
[] Folders
702 get { return folders; }
710 public DateTime LastAccessDate
712 get { return lastAccessDate; }
715 public string OwnerName
717 get { return ownerName; }
720 public VersionControlServer VersionControlServer
722 get { return versionControlServer; }
725 internal Repository Repository
727 get { return versionControlServer.Repository; }
730 internal void SetFileAttributes(string path
)
732 if (!readWriteSetting
)
733 File
.SetAttributes(path
, FileAttributes
.ReadOnly
);
736 internal void UnsetFileAttributes(string path
)
738 if (!readWriteSetting
)
739 File
.SetAttributes(path
, FileAttributes
.Normal
);