don't barf when diffing binary files
[tfs.git] / class / Microsoft.TeamFoundation.VersionControl.Client / DifferenceUtil.cs
blob1f9d18252fd4631c6b6c7f8490d50ca329dc0cfd
1 //
2 // Microsoft.TeamFoundation.VersionControl.Client.DifferenceUtil
3 //
4 // Authors:
5 // Joel Reed (joelwreed@gmail.com)
6 //
7 // Copyright (C) 2007 Joel Reed
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;
31 using System.Collections.Generic;
32 using System.IO;
33 using System.Net;
34 using System.Text;
35 using Microsoft.TeamFoundation.VersionControl.Common;
37 namespace Microsoft.TeamFoundation.VersionControl.Client
39 internal class DiffItemUtil
41 public string[] Lines = new string[0];
42 public string name;
43 public int Length;
45 public string Name
47 get {
48 return name;
52 public DiffItemUtil(char prefix, string name, IDiffItem file)
54 string fileContents = file.GetFile();
55 Length = fileContents.Length;
57 string path = PrefixedHeaderPath(prefix, name);
58 if (Length == 0)
60 this.name = "/dev/null";
61 return;
64 // gnu patch doesn't want to see backslashes in filenames
65 this.name = path.Replace('\\', '/');
67 // don't bother trying to parse lines for binary files
68 if (file.GetEncoding() == RepositoryConstants.EncodingBinary) return;
70 // if file ends with /n the split below generates one extra row we dont want
71 int len = fileContents.Length;
72 if (fileContents.EndsWith("\n")) len -= 1;
74 string x = fileContents.Substring(0, len);
75 Lines = x.Split('\n');
78 private string PrefixedHeaderPath(char c, string path)
80 StringBuilder sb = new StringBuilder();
82 sb.Append(c);
83 if (path[0] != Path.DirectorySeparatorChar) sb.Append(Path.DirectorySeparatorChar);
84 sb.Append(path);
86 return sb.ToString();
89 static public bool IsBinary(string s)
91 int size = Math.Min(8000, s.Length);
92 for (int i=0; i<size; i++)
94 if (s[i] == '\0')
95 return true;
98 return false;
102 internal class Hunk
104 static readonly int CONTEXT = 3;
106 private DiffItem item;
107 private int ctx1Start = 0;
108 private int ctx2Start = 0;
109 private int ctx1End = 0;
110 private int ctx2End = 0;
111 public int ctxLineCnt;
113 public DiffItem Item { get { return item; } }
115 public Hunk(DiffItem item, int prevDist, int nextDist,
116 int maxA, int maxB)
118 this.item = item;
120 ctx1Start = Math.Max(item.StartA - prevDist, 0);
121 int proposedEnd = Math.Min(item.StartA, ctx1Start + prevDist);
122 ctx1End = Math.Min(proposedEnd, maxA);
124 if (nextDist >= CONTEXT)
126 ctx2End = Math.Min(item.StartB + item.insertedB + nextDist, maxB);
127 int proposedStart = Math.Max(item.StartB + item.insertedB, ctx2End - nextDist);
128 //Console.WriteLine("proposedStart {0}, nextDist {1}", proposedStart, nextDist);
129 ctx2Start = Math.Min(proposedStart, ctx2End);
132 ctxLineCnt = (ctx1End - ctx1Start) + (ctx2End - ctx2Start);
133 //Console.WriteLine(String.Format("ctx1Start={0} ctx1End={1} ctx2Start={2} ctx2End={3} ctxLineCnt={4}\nmaxB={5} prevDist={6} nextDist={7}",
134 // ctx1Start, ctx1End, ctx2Start, ctx2End, ctxLineCnt, maxB, prevDist, nextDist));
137 public int LinesA { get { return ctxLineCnt + item.deletedA; } }
138 public int LinesB { get { return ctxLineCnt + item.insertedB; } }
140 public string ToString(string[] a, string[] b)
142 StringBuilder sb = new StringBuilder();
143 for (int i = ctx1Start; i < ctx1End; i++)
144 sb.Append(" " + a[i] + "\n");
146 for (int i = item.StartA; i < item.StartA + item.deletedA; i++)
147 sb.Append("-" + a[i] + "\n");
149 for (int i = item.StartB; i < item.StartB + item.insertedB; i++)
150 sb.Append("+" + b[i] + "\n");
152 for (int i = ctx2Start; i < ctx2End; i++)
153 sb.Append(" " + b[i] + "\n");
155 return sb.ToString();