usage.help
[tfs.git] / tools / opentf / DiffCommand.cs
bloba818794a37ecdce4f3c054169982d37b4cbb83ef
1 using System;
2 using System.Security.Cryptography;
3 using System.IO;
4 using System.Collections;
5 using System.Collections.Generic;
6 using System.Text;
7 using Microsoft.TeamFoundation.Client;
8 using Microsoft.TeamFoundation.VersionControl.Common;
9 using Microsoft.TeamFoundation.VersionControl.Client;
10 using Mono.GetOptions;
11 using OpenTF.Common;
13 internal class FromToHash
15 public FromToHash(int itemId) { this.ItemId = itemId; }
17 public int ItemId;
18 public int FromChangesetId;
19 public int ToChangesetId;
20 public string FromHash;
21 public string ToHash;
24 [Command("difference", "Show pending changes, latest on server, a changeset, or local changes not pended as a unified diff.",
25 "<path>...", "diff")]
26 class DifferenceCommand : Command
28 [Option("Output only whether files differ", "q", "brief")]
29 public bool OptionBrief = false;
31 [Option("Look for modified files", "", "modified")]
32 public bool OptionModified = false;
34 [Option("Show out of date files (newer version on server)", "", "old")]
35 public bool OptionOld = false;
37 [Option("Ignore white space differences", "", "ignorespace")]
38 public bool OptionIgnoreWhiteSpace = false;
40 [Option("Owner name", "O", "owner")]
41 public string OptionOwner;
43 private MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
45 public DifferenceCommand(Driver driver, string[] args): base(driver, args)
49 protected DiffOptions GetDiffOptions()
51 DiffOptions options = new DiffOptions();
52 options.UseThirdPartyTool = false;
54 options.Flags = DiffOptionFlags.EnablePreambleHandling;
55 if (OptionIgnoreWhiteSpace) options.Flags |= DiffOptionFlags.IgnoreWhiteSpace;
57 options.OutputType = DiffOutputType.Unified;
58 options.TargetEncoding = Console.OutputEncoding;
59 options.SourceEncoding = Console.OutputEncoding;
60 options.StreamWriter = new StreamWriter(Console.OpenStandardOutput(),
61 Console.OutputEncoding);
62 options.StreamWriter.AutoFlush = true;
64 return options;
67 public void Usage()
69 Console.WriteLine("Usage: tf diff [path | <changeset id> | <server path> <from versionSpec> <to versionSpec> | /old | /modified ]");
70 Environment.Exit((int)ExitCode.Failure);
73 VersionSpec GetValidVersionSpec(string arg)
75 VersionSpec versionSpec = null;
77 try
79 versionSpec = VersionSpec.ParseSingleSpec(arg, OwnerFromString(OptionOwner));
81 catch (System.FormatException exception)
83 Console.WriteLine("Invalid version specification: " + arg);
84 Environment.Exit((int)ExitCode.Failure);
87 return versionSpec;
90 public void ShowModifiedFiles(Workspace workspace, string path)
92 char[] charsToTrim = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};
93 string itemPath = path.TrimEnd(charsToTrim);
95 workspace.RefreshMappings();
96 string serverPath = workspace.GetServerItemForLocalItem(itemPath);
98 // pull item list based on WorkspaceVersion. otherwise might get
99 // new items on server that haven't been pulled yet in the list returned
100 WorkspaceVersionSpec version = new WorkspaceVersionSpec(workspace);
102 // get item list from TFS server
103 ItemSpec itemSpec = new ItemSpec(itemPath, RecursionType.Full);
104 ItemSet itemSet = VersionControlServer.GetItems(itemSpec, version, DeletedState.NonDeleted, ItemType.Any, true);
105 Item[] items = itemSet.Items;
107 foreach (Item item in items)
109 if (item.ItemType != ItemType.File) continue;
110 if (item.ServerItem.Length == serverPath.Length) continue;
111 string serverItem = item.ServerItem.Remove(0, serverPath.Length+1);
113 // server item paths are separated with '/', but on windows the file list below has '\' separated paths
114 if (Path.DirectorySeparatorChar != '/')
115 serverItem = serverItem.Replace('/', Path.DirectorySeparatorChar);
117 // only looking for modifications, not deletes or adds
118 string fname = Path.Combine(itemPath, serverItem);
119 if (!File.Exists(fname)) continue;
120 if (FileAttributes.ReadOnly == (File.GetAttributes(fname) & FileAttributes.ReadOnly))
121 continue;
123 using (FileStream fileStream = new FileStream(fname, FileMode.Open, FileAccess.Read))
125 string localHash = Convert.ToBase64String(md5.ComputeHash(fileStream));
126 string itemHash = Convert.ToBase64String(item.HashValue);
127 if (itemHash == localHash) continue;
130 string p = fname.Substring(itemPath.Length+1);
131 if (OptionBrief)
133 Driver.WriteLine(CanonicalPath(p));
134 continue;
137 string tnameA = Path.GetTempFileName();
138 item.DownloadFile(tnameA);
139 IDiffItem a = new DiffItemLocalFile(tnameA, item.Encoding, DateTime.Now, false);
141 IDiffItem b = new DiffItemLocalFile(fname, item.Encoding, DateTime.Now, false);
143 Difference.DiffFiles(VersionControlServer, a, b,
144 GetDiffOptions(), p, true);
146 if (!String.IsNullOrEmpty(tnameA)) File.Delete(tnameA);
150 public void ShowOldFiles(Workspace workspace, string path)
152 // process command options
153 ItemSpec itemSpec = new ItemSpec(path, RecursionType.Full);
155 List<ItemSpec> itemSpecs = new List<ItemSpec>();
156 itemSpecs.Add(itemSpec);
158 ExtendedItem[][] items = workspace.GetExtendedItems(itemSpecs.ToArray(),
159 DeletedState.NonDeleted, ItemType.Any);
161 foreach (ExtendedItem[] itemArray in items)
163 foreach (ExtendedItem item in itemArray)
165 if (item.IsLatest) continue;
167 string p = item.LocalItem.Substring(path.Length);
168 if (OptionBrief)
170 Driver.WriteLine(p);
171 continue;
174 IDiffItem a = new DiffItemNull();
175 IDiffItem b = new DiffItemNull();
177 if ((item.ChangeType & ChangeType.Add) != ChangeType.Add)
179 a = new DiffItemLocalFile(item.LocalItem, item.Encoding,
180 DateTime.Now, false);
183 if ((item.ChangeType & ChangeType.Delete) != ChangeType.Delete)
185 b = new DiffItemVersionedFile(VersionControlServer,
186 item.ItemId, item.VersionLatest, item.LocalItem);
189 Difference.DiffFiles(VersionControlServer, a, b,
190 GetDiffOptions(), p, true);
195 void ShowChangesBetweenVersions(VersionSpec fromVersionSpec,
196 VersionSpec toVersionSpec, string serverPath)
198 //Console.WriteLine(fromVersionSpec.ToString());
199 //Console.WriteLine(toVersionSpec.ToString());
200 //Console.WriteLine("serverPath= " + serverPath);
202 SortedList<string, FromToHash> itemHashes = new SortedList<string, FromToHash>(StringComparer.CurrentCultureIgnoreCase);
204 ItemSet fromItemSet = VersionControlServer.GetItems(serverPath, fromVersionSpec,
205 RecursionType.Full);
206 Item[] fromItems = fromItemSet.Items;
208 foreach (Item item in fromItems)
210 if (item.ItemType != ItemType.File) continue;
211 if (item.ServerItem.Length == serverPath.Length) continue;
213 FromToHash fromToHash = new FromToHash(item.ItemId);
214 fromToHash.FromHash = Convert.ToBase64String(item.HashValue);
215 fromToHash.FromChangesetId = item.ChangesetId;
217 itemHashes.Add(item.ServerItem, fromToHash);
220 ItemSet toItemSet = VersionControlServer.GetItems(serverPath, toVersionSpec,
221 RecursionType.Full);
222 Item[] toItems = toItemSet.Items;
224 foreach (Item item in toItems)
226 if (item.ItemType != ItemType.File) continue;
227 if (item.ServerItem.Length == serverPath.Length) continue;
229 string hash = Convert.ToBase64String(item.HashValue);
230 if (!itemHashes.ContainsKey(item.ServerItem))
232 FromToHash fromToHash = new FromToHash(item.ItemId);
233 fromToHash.ToHash = hash;
234 fromToHash.ToChangesetId = item.ChangesetId;
235 itemHashes.Add(item.ServerItem, fromToHash);
237 else
239 FromToHash fromToHash = itemHashes[item.ServerItem];
240 fromToHash.ToHash = hash;
241 fromToHash.ToChangesetId = item.ChangesetId;
245 foreach (string key in itemHashes.Keys)
247 FromToHash fromToHash = itemHashes[key];
248 if (fromToHash.FromHash == fromToHash.ToHash) continue;
250 IDiffItem a = new DiffItemNull();
251 IDiffItem b = new DiffItemNull();
253 if (!String.IsNullOrEmpty(fromToHash.FromHash))
255 a = new DiffItemVersionedFile(VersionControlServer,
256 fromToHash.ItemId, fromToHash.FromChangesetId, key);
259 if (!String.IsNullOrEmpty(fromToHash.ToHash))
261 b = new DiffItemVersionedFile(VersionControlServer,
262 fromToHash.ItemId, fromToHash.ToChangesetId, key);
265 string p = key.Substring(2);
266 Difference.DiffFiles(VersionControlServer, a, b,
267 GetDiffOptions(), p, true);
271 void ShowPendingChanges(Workspace workspace, string[] paths)
273 PendingChange[] pendingChanges = workspace.GetPendingChanges(paths, RecursionType.Full, true);
274 if (pendingChanges.Length == 0)
276 Console.WriteLine("No pending changes.");
277 Environment.Exit((int)ExitCode.PartialSuccess);
280 string cwd = Environment.CurrentDirectory;
281 foreach (PendingChange change in pendingChanges)
283 string p = change.LocalItem;
284 if (p.StartsWith(cwd)) p = p.Substring(cwd.Length+1);
286 if (OptionBrief)
288 Driver.WriteLine(CanonicalPath(p));
289 continue;
292 IDiffItem a = new DiffItemNull();
293 IDiffItem b = new DiffItemNull();
295 string tname = null;
296 if (!change.IsAdd)
298 tname = Path.GetTempFileName();
299 change.DownloadBaseFile(tname);
301 a = new DiffItemLocalFile(tname, change.Encoding,
302 change.CreationDate, true);
305 if (!change.IsDelete)
307 b = new DiffItemLocalFile(change.LocalItem, change.Encoding,
308 change.CreationDate, false);
311 Difference.DiffFiles(VersionControlServer, a, b,
312 GetDiffOptions(), p, true);
314 if (!String.IsNullOrEmpty(tname))
315 File.Delete(tname);
319 public void ProcessOldAndModified(Workspace workspace)
321 string path = Environment.CurrentDirectory;
322 if (Arguments.Length > 0)
324 path = Path.GetFullPath(Arguments[0]);
327 if (OptionOld) ShowOldFiles(workspace, path);
328 else ShowModifiedFiles(workspace, path);
331 public override void Run()
333 Workspace workspace = GetWorkspaceFromCache();
334 if (OptionOld || OptionModified)
336 ProcessOldAndModified(workspace);
337 Environment.Exit((int)ExitCode.Success);
340 if (Arguments.Length == 0 || File.Exists(Arguments[0])
341 || Directory.Exists(Arguments[0]))
343 List<string> paths;
344 paths = VerifiedFullPaths(Arguments);
345 if (paths.Count == 0) paths.Add(Environment.CurrentDirectory);
346 ShowPendingChanges(workspace, paths.ToArray());
347 Environment.Exit((int)ExitCode.Success);
350 if (Arguments.Length == 1)
352 string arg = Arguments[0];
353 VersionSpec versionSpec = GetValidVersionSpec(arg);
355 if (versionSpec is ChangesetVersionSpec)
356 DiffHelper.ShowChangeset(VersionControlServer,
357 versionSpec as ChangesetVersionSpec,
358 OptionBrief, GetDiffOptions());
359 Environment.Exit((int)ExitCode.Success);
362 VersionSpec fromVersionSpec = GetValidVersionSpec(Arguments[0]);
363 VersionSpec toVersionSpec = GetValidVersionSpec(Arguments[1]);
365 if (fromVersionSpec == null || toVersionSpec == null)
366 Usage();
368 string localPath = Environment.CurrentDirectory;
369 string serverPath = null;
370 if (Arguments.Length > 2)
372 if (VersionControlPath.IsServerItem(Arguments[2]))
373 serverPath = Arguments[2];
374 else
375 localPath = Path.GetFullPath(Arguments[2]);
378 if (String.IsNullOrEmpty(serverPath))
380 workspace.RefreshMappings();
381 serverPath = workspace.GetServerItemForLocalItem(localPath);
384 ShowChangesBetweenVersions(fromVersionSpec, toVersionSpec, serverPath);