don't try to upload new directories on checkin, only files
[tfs.git] / class / Microsoft.TeamFoundation.VersionControl.Client / Workspace.cs
blob9d5a129000f456bb60bd34628b7b79f13cc6414a
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>();
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];
345 paths[0] = path;
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);
373 updates.Flush();
374 return operations.Length;
377 public int PendEdit(string path)
379 // Annotation[] annotations = Repository.QueryAnnotation("ExclusiveCheckout",
380 // path, 0);
381 // foreach (Annotation annotation in annotations)
382 // {
383 // Console.WriteLine(annotation.ToString());
384 // }
386 return PendEdit(path, RecursionType.None);
389 public int PendEdit(string path, RecursionType recursionType)
391 string[] paths = new string[1];
392 paths[0] = path;
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);
435 updates.Flush();
436 return 1;
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");
447 string comment = "";
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)
454 break;
456 if (reader.NodeType == XmlNodeType.Element)
458 switch (reader.Name)
460 case "WorkingFolder":
461 folders.Add(WorkingFolder.FromXml(repository, reader));
462 break;
463 case "Comment":
464 comment = reader.ReadString();
465 break;
466 case "LastAccessDate":
467 lastAccessDate = reader.ReadElementContentAsDateTime();
468 break;
473 Workspace w = new Workspace(repository.VersionControlServer, name, owner, comment, folders.ToArray(), computer);
474 w.lastAccessDate = lastAccessDate;
475 return w;
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);
486 if (folders != null)
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: ");
509 sb.Append(Comment);
511 sb.Append("\n LastAccessDate: ");
512 sb.Append(LastAccessDate);
514 sb.Append("\n Folders: ");
515 if (folders != null)
517 foreach (WorkingFolder folder in folders)
519 sb.Append(folder.ToString());
523 sb.Append("\n Computer: ");
524 sb.Append(Computer);
526 sb.Append("\n Name: ");
527 sb.Append(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);
544 return null;
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);
556 return null;
559 public WorkingFolder TryGetWorkingFolderForServerItem(string serverItem)
561 int maxPath = 0;
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];
586 paths[0] = path;
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,
632 w2, newMappings);
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; }
658 public string Name
660 get { return name; }
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; }