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
;
50 internal Workspace(VersionControlServer versionControlServer
, string name
,
51 string ownerName
, string comment
,
52 WorkingFolder
[] folders
, string computer
)
54 this.versionControlServer
= versionControlServer
;
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
);
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];
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
,
138 foreach (Failure failure
in failures
)
140 Console
.WriteLine(failure
.ToString());
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
)
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
);
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
);
258 return new GetStatus(getOperations
.Length
);
261 public ExtendedItem
[][] GetExtendedItems (ItemSpec
[] itemSpecs
,
262 DeletedState deletedState
,
265 return Repository
.QueryItemsExtended(Name
, OwnerName
,
266 itemSpecs
, deletedState
, itemType
);
269 public string GetServerItemForLocalItem(string localItem
)
271 string item
= TryGetServerItemForLocalItem(localItem
);
273 throw new ItemNotMappedException(localItem
);
277 public string GetLocalItemForServerItem(string serverItem
)
279 string item
= TryGetLocalItemForServerItem(serverItem
);
281 throw new ItemNotMappedException(serverItem
);
285 public bool IsLocalPathMapped(string localPath
)
287 foreach (WorkingFolder workingFolder
in Folders
)
289 if (workingFolder
.LocalItem
== localPath
) return true;
295 public bool IsServerPathMapped(string serverPath
)
297 foreach (WorkingFolder workingFolder
in Folders
)
299 if (workingFolder
.ServerItem
== serverPath
) return true;
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];
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];
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
);
385 Directory
.Delete(operation
.SourceLocalItem
);
388 updates
.QueueUpdate(operation
.ItemId
, null, operation
.VersionServer
);
392 return operations
.Length
;
395 public int PendEdit(string path
)
397 // Annotation[] annotations = Repository.QueryAnnotation("ExclusiveCheckout",
399 // foreach (Annotation annotation in annotations)
401 // Console.WriteLine(annotation.ToString());
404 return PendEdit(path
, RecursionType
.None
);
407 public int PendEdit(string path
, RecursionType recursionType
)
409 string[] paths
= new string[1];
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
);
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");
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
)
477 if (reader
.NodeType
== XmlNodeType
.Element
)
481 case "WorkingFolder":
482 folders
.Add(WorkingFolder
.FromXml(repository
, reader
));
485 comment
= reader
.ReadString();
487 case "LastAccessDate":
488 lastAccessDate
= reader
.ReadElementContentAsDateTime();
494 Workspace w
= new Workspace(repository
.VersionControlServer
, name
, owner
, comment
, folders
.ToArray(), computer
);
495 w
.lastAccessDate
= lastAccessDate
;
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
);
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: ");
532 sb
.Append("\n LastAccessDate: ");
533 sb
.Append(LastAccessDate
);
535 sb
.Append("\n Folders: ");
538 foreach (WorkingFolder folder
in folders
)
540 sb
.Append(folder
.ToString());
544 sb
.Append("\n Computer: ");
547 sb
.Append("\n 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
);
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
);
580 public WorkingFolder
TryGetWorkingFolderForServerItem(string serverItem
)
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];
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
,
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; }
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; }