root.node
[tfs.git] / tools / tf / OnlineCommand.cs
blob62bd083e7a7286d591b0f049ce3a652a72eff056
1 //
2 // OnlineCommand.cs
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.Security.Cryptography;
31 using System.Collections.Generic;
32 using System.IO;
33 using System.Text;
34 using System.Text.RegularExpressions;
35 using Microsoft.TeamFoundation.Client;
36 using Microsoft.TeamFoundation.VersionControl.Common;
37 using Microsoft.TeamFoundation.VersionControl.Client;
38 using Mono.GetOptions;
40 [Command("online", "Finds all writable files and marks them as pending changes on the server.")]
41 class OnlineCommand : Command
43 [Option("Look for added files.", "", "added")]
44 public bool OptionAdded = false;
46 [Option("Look for deleted files.", "D", "deleted")]
47 public bool OptionDeleted = false;
49 [Option("Look for modified files.", "", "modified")]
50 public bool OptionModified = false;
52 [Option("Preview", "", "preview")]
53 public bool OptionPreview = false;
55 [Option("Recursive", "R", "recursive")]
56 public bool OptionRecursive = false;
58 private List<string> addedFiles = new List<string>();
59 private List<string> editedFiles = new List<string>();
60 private List<string> deletedFiles = new List<string>();
61 private Workspace workspace;
62 private MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
64 public OnlineCommand(Driver driver, string[] args): base(driver, args)
68 private void ProcessFile(SortedList<string, byte[]> itemList, string path)
70 bool isReadOnly = (FileAttributes.ReadOnly == (File.GetAttributes(path) & FileAttributes.ReadOnly));
72 if (!itemList.ContainsKey(path))
74 if (OptionAdded)
76 Console.WriteLine("Added: " + path);
77 addedFiles.Add(path);
80 else if (!isReadOnly && OptionModified)
82 string itemHash = Convert.ToBase64String(itemList[path]);
84 using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
86 md5.ComputeHash(fileStream);
87 string localHash = Convert.ToBase64String(md5.Hash);
88 if (itemHash != localHash)
90 editedFiles.Add(path);
91 Console.WriteLine("Modified: " + path);
97 private void ProcessFileList(SortedList<string, bool> files)
99 List<ItemSpec> itemSpecs = new List<ItemSpec>();
100 foreach (string file in files.Keys)
101 itemSpecs.Add(new ItemSpec(file, RecursionType.None));
103 // pull item list based on WorkspaceVersion. otherwise might get
104 // new items on server that haven't been pulled yet in the list returned
105 WorkspaceVersionSpec version = new WorkspaceVersionSpec(workspace);
106 SortedList<string, byte[] > itemList = new SortedList<string, byte[] >(PathComparer);
108 // get item list from TFS server
109 ItemSet[] itemSets = VersionControlServer.GetItems(itemSpecs.ToArray(), version, DeletedState.NonDeleted, ItemType.File, true);
110 foreach (ItemSet itemSet in itemSets)
112 foreach (Item item in itemSet.Items)
114 string localItem = workspace.GetLocalItemForServerItem(item.ServerItem);
115 itemList.Add(localItem, item.HashValue);
119 // process adds and edits
120 foreach (string file in files.Keys)
122 // skip files we're not interested in here
123 if (IsExcludedFile(file)) continue;
124 if (!File.Exists(file)) continue;
126 ProcessFile(itemList, file);
129 if (!OptionDeleted) return;
131 // now process deletes
132 foreach (string key in itemList.Keys)
134 // skip files that exist or we're not interested in
135 if (files.ContainsKey(key)) continue;
136 if (IsExcludedFile(key)) continue;
138 Console.WriteLine("Deleted: " + key);
139 deletedFiles.Add(key);
143 private void ProcessDirectory(string path)
145 char[] charsToTrim = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};
146 string itemPath = path.TrimEnd(charsToTrim);
147 string serverPath = workspace.GetServerItemForLocalItem(itemPath);
149 // pull item list based on WorkspaceVersion. otherwise might get
150 // new items on server that haven't been pulled yet in the list returned
151 WorkspaceVersionSpec version = new WorkspaceVersionSpec(workspace);
153 // process recursion settings
154 RecursionType rtype = OptionRecursive ? RecursionType.Full : RecursionType.OneLevel;
155 bool recursionSetting = Settings.Current.GetAsBool("Online.Recursive");
156 if (recursionSetting) rtype = RecursionType.Full;
157 SearchOption searchType = (rtype == RecursionType.Full) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
159 // process command options
160 ItemSpec itemSpec = new ItemSpec(itemPath, rtype);
161 ItemSet itemSet = VersionControlServer.GetItems(itemSpec, version, DeletedState.NonDeleted, ItemType.Any, true);
163 // get item list from TFS server
164 Item[] items = itemSet.Items;
165 SortedList<string, byte[] > itemList = new SortedList<string, byte[] >(PathComparer);
167 foreach (Item item in items)
169 if (item.ServerItem.Length == serverPath.Length) continue;
170 string serverItem = item.ServerItem.Remove(0, serverPath.Length+1);
172 // server item paths are separated with '/', but on windows the file list below has '\' separated paths
173 if (Path.DirectorySeparatorChar != '/')
174 serverItem = serverItem.Replace('/', Path.DirectorySeparatorChar);
176 string fname = Path.Combine(itemPath, serverItem);
177 //Console.WriteLine(serverItem + " : " + fname);
179 itemList.Add(fname, item.HashValue);
182 DirectoryInfo dir = new DirectoryInfo(path);
183 FileInfo[] localFiles = dir.GetFiles("*", searchType);
185 SortedList<string, bool> dirList = new SortedList<string, bool>();
186 foreach (FileInfo file in localFiles)
188 // skip files we're not interested in
189 if (IsExcludedFile(file.Name)) continue;
191 dirList.Add(file.FullName, true);
192 ProcessFile(itemList, file.FullName);
195 foreach (DirectoryInfo di in dir.GetDirectories("*", SearchOption.AllDirectories))
197 dirList.Add(di.FullName, true);
200 if (!OptionDeleted) return;
202 foreach (string key in itemList.Keys)
204 // skip files that exist or we're not interested in
205 if (dirList.ContainsKey(key)) continue;
206 if (IsExcludedFile(key)) continue;
208 Console.WriteLine("Deleted: " + key);
209 deletedFiles.Add(key);
213 private void Online(string[] args)
215 if (args.Length == 0)
217 ProcessDirectory(Environment.CurrentDirectory);
218 return;
221 SortedList<string, bool> files = new SortedList<string, bool>();
222 foreach (string arg in args)
224 string path = Path.GetFullPath(arg);
225 if (Directory.Exists(path)) ProcessDirectory(path);
226 else files.Add(path, true);
229 if (files.Count > 0) ProcessFileList(files);
232 public override void Run()
234 // must get server<->local mappings for GetServerItemForLocalItem
235 workspace = GetWorkspaceFromCache();
236 workspace.RefreshMappings();
238 // by default, if nothing specified we process all changes
239 if ((!OptionModified) && (!OptionDeleted) && (!OptionAdded))
241 OptionModified = OptionAdded = OptionDeleted = true;
244 Online(Arguments);
245 if (OptionPreview) return;
247 int changes = 0;
248 changes += workspace.PendAdd(addedFiles.ToArray(), false);
249 changes += workspace.PendEdit(editedFiles.ToArray(), RecursionType.None);
250 changes += workspace.PendDelete(deletedFiles.ToArray(), RecursionType.None);
251 Console.WriteLine("{0} pending changes.", changes);