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
>();
326 foreach (string path
in paths
)
328 changes
.Add(new ChangeRequest(path
, RequestType
.Add
));
331 if (changes
.Count
== 0) return 0;
333 GetOperation
[] operations
= Repository
.PendChanges(this, changes
.ToArray());
334 return operations
.Length
;
337 public int PendDelete(string path
)
339 return PendDelete(path
, RecursionType
.None
);
342 public int PendDelete(string path
, RecursionType recursionType
)
344 string[] paths
= new string[1];
347 return PendDelete(paths
, recursionType
);
350 public int PendDelete(string[] paths
, RecursionType recursionType
)
352 List
<ChangeRequest
> changes
= new List
<ChangeRequest
>();
353 foreach (string path
in paths
)
355 changes
.Add(new ChangeRequest(path
, RequestType
.Delete
));
358 if (changes
.Count
== 0) return 0;
360 GetOperation
[] operations
= Repository
.PendChanges(this, changes
.ToArray());
361 UpdateLocalVersionQueue updates
= new UpdateLocalVersionQueue(this);
363 foreach (GetOperation operation
in operations
)
365 if (File
.Exists(operation
.SourceLocalItem
))
367 File
.SetAttributes(operation
.SourceLocalItem
, FileAttributes
.Normal
);
368 File
.Delete(operation
.SourceLocalItem
);
369 updates
.QueueUpdate(operation
.ItemId
, null, operation
.VersionServer
);
374 return operations
.Length
;
377 public int PendEdit(string path
)
379 // Annotation[] annotations = Repository.QueryAnnotation("ExclusiveCheckout",
381 // foreach (Annotation annotation in annotations)
383 // Console.WriteLine(annotation.ToString());
386 return PendEdit(path
, RecursionType
.None
);
389 public int PendEdit(string path
, RecursionType recursionType
)
391 string[] paths
= new string[1];
394 return PendEdit(paths
, recursionType
);
397 public int PendEdit(string[] paths
, RecursionType recursionType
)
399 List
<ChangeRequest
> changes
= new List
<ChangeRequest
>();
400 foreach (string path
in paths
)
402 changes
.Add(new ChangeRequest(path
, RequestType
.Edit
));
405 if (changes
.Count
== 0) return 0;
407 GetOperation
[] getOperations
= Repository
.PendChanges(this, changes
.ToArray());
409 foreach (GetOperation getOperation
in getOperations
)
411 File
.SetAttributes(getOperation
.TargetLocalItem
, FileAttributes
.Normal
);
414 return getOperations
.Length
;
417 public int PendRename(string oldPath
, string newPath
)
419 string newServerPath
;
420 if (VersionControlPath
.IsServerItem(newPath
)) newServerPath
= GetServerItemForLocalItem(newPath
);
421 else newServerPath
= newPath
;
423 List
<ChangeRequest
> changes
= new List
<ChangeRequest
>();
424 changes
.Add(new ChangeRequest(oldPath
, newServerPath
, RequestType
.Rename
));
426 GetOperation
[] getOperations
= Repository
.PendChanges(this, changes
.ToArray());
428 UpdateLocalVersionQueue updates
= new UpdateLocalVersionQueue(this);
429 foreach (GetOperation getOperation
in getOperations
)
431 File
.Move(oldPath
, getOperation
.TargetLocalItem
);
432 updates
.QueueUpdate(getOperation
.ItemId
, getOperation
.TargetLocalItem
, getOperation
.VersionServer
);
439 internal static Workspace
FromXml(Repository repository
, XmlReader reader
)
441 string elementName
= reader
.Name
;
443 string computer
= reader
.GetAttribute("computer");
444 string name
= reader
.GetAttribute("name");
445 string owner
= reader
.GetAttribute("owner");
448 DateTime lastAccessDate
= DateTime
.Now
;
449 List
<WorkingFolder
> folders
= new List
<WorkingFolder
>();
451 while (reader
.Read())
453 if (reader
.NodeType
== XmlNodeType
.EndElement
&& reader
.Name
== elementName
)
456 if (reader
.NodeType
== XmlNodeType
.Element
)
460 case "WorkingFolder":
461 folders
.Add(WorkingFolder
.FromXml(repository
, reader
));
464 comment
= reader
.ReadString();
466 case "LastAccessDate":
467 lastAccessDate
= reader
.ReadElementContentAsDateTime();
473 Workspace w
= new Workspace(repository
.VersionControlServer
, name
, owner
, comment
, folders
.ToArray(), computer
);
474 w
.lastAccessDate
= lastAccessDate
;
478 internal void ToXml(XmlWriter writer
, string element
)
480 writer
.WriteStartElement(element
);
481 writer
.WriteAttributeString("computer", Computer
);
482 writer
.WriteAttributeString("name", Name
);
483 writer
.WriteAttributeString("owner", OwnerName
);
484 writer
.WriteElementString("Comment", Comment
);
488 writer
.WriteStartElement("Folders");
490 foreach (WorkingFolder folder
in folders
)
492 folder
.ToXml(writer
, element
);
495 writer
.WriteEndElement();
498 writer
.WriteEndElement();
501 public override string ToString()
503 StringBuilder sb
= new StringBuilder();
505 sb
.Append("Workspace instance ");
506 sb
.Append(GetHashCode());
508 sb
.Append("\n Comment: ");
511 sb
.Append("\n LastAccessDate: ");
512 sb
.Append(LastAccessDate
);
514 sb
.Append("\n Folders: ");
517 foreach (WorkingFolder folder
in folders
)
519 sb
.Append(folder
.ToString());
523 sb
.Append("\n Computer: ");
526 sb
.Append("\n Name: ");
529 sb
.Append("\n OwnerName: ");
530 sb
.Append(OwnerName
);
532 return sb
.ToString();
535 public string TryGetServerItemForLocalItem(string localItem
)
537 foreach (WorkingFolder folder
in folders
)
539 if (!localItem
.StartsWith(folder
.LocalItem
, StringComparison
.InvariantCultureIgnoreCase
)) continue;
540 string suffix
= localItem
.Substring(folder
.LocalItem
.Length
);
541 return String
.Format("{0}{1}", folder
.ServerItem
, suffix
);
547 public string TryGetLocalItemForServerItem(string serverItem
)
549 foreach (WorkingFolder folder
in folders
)
551 if (!serverItem
.StartsWith(folder
.ServerItem
, StringComparison
.InvariantCultureIgnoreCase
)) continue;
552 string suffix
= serverItem
.Substring(folder
.ServerItem
.Length
);
553 return String
.Format("{0}{1}", folder
.LocalItem
, suffix
);
559 public WorkingFolder
TryGetWorkingFolderForServerItem(string serverItem
)
562 WorkingFolder workingFolder
= null;
564 foreach (WorkingFolder folder
in folders
)
566 if (!serverItem
.StartsWith(folder
.ServerItem
, StringComparison
.InvariantCultureIgnoreCase
)) continue;
568 if (folder
.LocalItem
.Length
> maxPath
)
570 workingFolder
= folder
;
571 maxPath
= folder
.LocalItem
.Length
;
575 return workingFolder
;
578 public int Undo(string path
)
580 return Undo(path
, RecursionType
.None
);
583 public int Undo(string path
, RecursionType recursionType
)
585 string[] paths
= new string[1];
587 return Undo(paths
, recursionType
);
590 public int Undo(string[] paths
, RecursionType recursionType
)
592 List
<ItemSpec
> specs
= new List
<ItemSpec
>();
594 foreach (string path
in paths
)
596 specs
.Add(new ItemSpec(path
, recursionType
));
599 // is this the same logic as a workspace Get?
600 // can we make one function to handle both cases?
602 GetOperation
[] getOperations
= Repository
.UndoPendingChanges(Name
, OwnerName
, specs
.ToArray());
603 foreach (GetOperation getOperation
in getOperations
)
605 if (getOperation
.ChangeType
== ChangeType
.Edit
||
606 getOperation
.ChangeType
== ChangeType
.Delete
)
608 string uPath
= getOperation
.TargetLocalItem
;
609 string directory
= uPath
;
611 if (getOperation
.ItemType
== ItemType
.File
)
612 directory
= Path
.GetDirectoryName(uPath
);
614 if (!Directory
.Exists(directory
))
615 Directory
.CreateDirectory(directory
);
617 if (getOperation
.ItemType
== ItemType
.File
)
618 DownloadFile
.WriteTo(uPath
, Repository
, getOperation
.ArtifactUri
);
622 return getOperations
.Length
;
625 public void Update (string newName
, string newComment
, WorkingFolder
[] newMappings
)
627 Workspace w1
= new Workspace(VersionControlServer
, newName
, OwnerName
,
628 newComment
, newMappings
, Computer
);
629 Workspace w2
= Repository
.UpdateWorkspace(Name
, OwnerName
, w1
);
631 Workstation
.Current
.UpdateCachedWorkspaceInfo(VersionControlServer
.Uri
,
634 folders
= w2
.Folders
;
637 public void RefreshMappings ()
639 Workspace w
= Repository
.QueryWorkspace(Name
, OwnerName
);
640 this.folders
= w
.folders
;
643 public string Comment
645 get { return comment; }
648 public string Computer
650 get { return computer; }
653 public WorkingFolder
[] Folders
655 get { return folders; }
663 public DateTime LastAccessDate
665 get { return lastAccessDate; }
668 public string OwnerName
670 get { return ownerName; }
673 public VersionControlServer VersionControlServer
675 get { return versionControlServer; }
678 internal Repository Repository
680 get { return versionControlServer.Repository; }