2 using System
.Security
.Cryptography
;
4 using System
.Collections
;
5 using System
.Collections
.Generic
;
7 using Microsoft
.TeamFoundation
.Client
;
8 using Microsoft
.TeamFoundation
.VersionControl
.Common
;
9 using Microsoft
.TeamFoundation
.VersionControl
.Client
;
10 using Mono
.GetOptions
;
13 internal class FromToHash
15 public FromToHash(int itemId
) { this.ItemId = itemId; }
18 public int FromChangesetId
;
19 public int ToChangesetId
;
20 public string FromHash
;
24 [Command("difference", "Show pending changes, latest on server, a changeset, or local changes not pended as a unified 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;
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;
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
);
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
))
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);
133 Driver
.WriteLine(CanonicalPath(p
));
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
);
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 SortedList
<string, FromToHash
> itemHashes
= new SortedList
<string, FromToHash
>(StringComparer
.CurrentCultureIgnoreCase
);
200 ItemSet fromItemSet
= VersionControlServer
.GetItems(serverPath
, fromVersionSpec
,
202 Item
[] fromItems
= fromItemSet
.Items
;
204 foreach (Item item
in fromItems
)
206 if (item
.ItemType
!= ItemType
.File
) continue;
207 if (item
.ServerItem
.Length
== serverPath
.Length
) continue;
209 FromToHash fromToHash
= new FromToHash(item
.ItemId
);
210 fromToHash
.FromHash
= Convert
.ToBase64String(item
.HashValue
);
211 fromToHash
.FromChangesetId
= item
.ChangesetId
;
213 itemHashes
.Add(item
.ServerItem
, fromToHash
);
216 ItemSet toItemSet
= VersionControlServer
.GetItems(serverPath
, toVersionSpec
,
218 Item
[] toItems
= toItemSet
.Items
;
220 foreach (Item item
in toItems
)
222 if (item
.ItemType
!= ItemType
.File
) continue;
223 if (item
.ServerItem
.Length
== serverPath
.Length
) continue;
225 string hash
= Convert
.ToBase64String(item
.HashValue
);
226 if (!itemHashes
.ContainsKey(item
.ServerItem
))
228 FromToHash fromToHash
= new FromToHash(item
.ItemId
);
229 fromToHash
.ToHash
= hash
;
230 fromToHash
.ToChangesetId
= item
.ChangesetId
;
231 itemHashes
.Add(item
.ServerItem
, fromToHash
);
235 FromToHash fromToHash
= itemHashes
[item
.ServerItem
];
236 fromToHash
.ToHash
= hash
;
237 fromToHash
.ToChangesetId
= item
.ChangesetId
;
241 foreach (string key
in itemHashes
.Keys
)
243 FromToHash fromToHash
= itemHashes
[key
];
244 if (fromToHash
.FromHash
== fromToHash
.ToHash
) continue;
248 Driver
.WriteLine(key
);
252 IDiffItem a
= new DiffItemNull();
253 IDiffItem b
= new DiffItemNull();
255 if (!String
.IsNullOrEmpty(fromToHash
.FromHash
))
257 a
= new DiffItemVersionedFile(VersionControlServer
,
258 fromToHash
.ItemId
, fromToHash
.FromChangesetId
, key
);
261 if (!String
.IsNullOrEmpty(fromToHash
.ToHash
))
263 b
= new DiffItemVersionedFile(VersionControlServer
,
264 fromToHash
.ItemId
, fromToHash
.ToChangesetId
, key
);
267 string p
= key
.Substring(2);
268 Difference
.DiffFiles(VersionControlServer
, a
, b
,
269 GetDiffOptions(), p
, true);
273 void ShowPendingChanges(Workspace workspace
, string[] paths
)
275 PendingChange
[] pendingChanges
= workspace
.GetPendingChanges(paths
, RecursionType
.Full
, true);
276 if (pendingChanges
.Length
== 0)
278 Console
.WriteLine("No pending changes.");
279 Environment
.Exit((int)ExitCode
.PartialSuccess
);
282 string cwd
= Environment
.CurrentDirectory
;
283 foreach (PendingChange change
in pendingChanges
)
285 string p
= change
.LocalItem
;
286 if (p
.StartsWith(cwd
)) p
= p
.Substring(cwd
.Length
+1);
290 Driver
.WriteLine(CanonicalPath(p
));
294 IDiffItem a
= new DiffItemNull();
295 IDiffItem b
= new DiffItemNull();
300 tname
= Path
.GetTempFileName();
301 change
.DownloadBaseFile(tname
);
303 a
= new DiffItemLocalFile(tname
, change
.Encoding
,
304 change
.CreationDate
, true);
307 if (!change
.IsDelete
)
309 b
= new DiffItemLocalFile(change
.LocalItem
, change
.Encoding
,
310 change
.CreationDate
, false);
313 Difference
.DiffFiles(VersionControlServer
, a
, b
,
314 GetDiffOptions(), p
, true);
316 if (!String
.IsNullOrEmpty(tname
))
321 public void ProcessOldAndModified(Workspace workspace
)
323 string path
= Environment
.CurrentDirectory
;
324 if (Arguments
.Length
> 0)
326 path
= Path
.GetFullPath(Arguments
[0]);
329 if (OptionOld
) ShowOldFiles(workspace
, path
);
330 else ShowModifiedFiles(workspace
, path
);
333 public override void Run()
335 Workspace workspace
= GetWorkspaceFromCache();
336 if (OptionOld
|| OptionModified
)
338 ProcessOldAndModified(workspace
);
339 Environment
.Exit((int)ExitCode
.Success
);
342 if (Arguments
.Length
== 0 || File
.Exists(Arguments
[0])
343 || Directory
.Exists(Arguments
[0]))
346 paths
= VerifiedFullPaths(Arguments
);
347 if (paths
.Count
== 0) paths
.Add(Environment
.CurrentDirectory
);
348 ShowPendingChanges(workspace
, paths
.ToArray());
349 Environment
.Exit((int)ExitCode
.Success
);
352 if (Arguments
.Length
== 1)
354 string arg
= Arguments
[0];
355 VersionSpec versionSpec
= GetValidVersionSpec(arg
);
357 if (versionSpec
is ChangesetVersionSpec
)
358 DiffHelper
.ShowChangeset(VersionControlServer
,
359 versionSpec
as ChangesetVersionSpec
,
360 OptionBrief
, GetDiffOptions());
361 Environment
.Exit((int)ExitCode
.Success
);
364 VersionSpec fromVersionSpec
= GetValidVersionSpec(Arguments
[0]);
365 VersionSpec toVersionSpec
= GetValidVersionSpec(Arguments
[1]);
367 if (fromVersionSpec
== null || toVersionSpec
== null)
370 string localPath
= Environment
.CurrentDirectory
;
371 string serverPath
= null;
372 if (Arguments
.Length
> 2)
374 if (VersionControlPath
.IsServerItem(Arguments
[2]))
375 serverPath
= Arguments
[2];
377 localPath
= Path
.GetFullPath(Arguments
[2]);
380 if (String
.IsNullOrEmpty(serverPath
))
382 workspace
.RefreshMappings();
383 serverPath
= workspace
.GetServerItemForLocalItem(localPath
);
386 ShowChangesBetweenVersions(fromVersionSpec
, toVersionSpec
, serverPath
);