PageRenderTime 55ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/GitCommands/CommitData.cs

https://github.com/eisnerd/gitextensions
C# | 294 lines | 213 code | 57 blank | 24 comment | 38 complexity | 0a787f5607a6179935d6768c16d7c722 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Web;
  7. namespace GitCommands
  8. {
  9. public class CommitData
  10. {
  11. private const string COMMIT_LABEL = "commit ";
  12. private const string TREE_LABEL = "tree ";
  13. private const string PARENT_LABEL = "parent ";
  14. private const string AUTHOR_LABEL = "author ";
  15. private const string COMMITTER_LABEL = "committer ";
  16. private const int COMMITHEADER_STRING_LENGTH = 16;
  17. /// <summary>
  18. /// Private constructor
  19. /// </summary>
  20. private CommitData(string guid,
  21. string treeGuid, ReadOnlyCollection<string> parentGuids,
  22. string author, DateTimeOffset authorDate,
  23. string committer, DateTimeOffset commitDate,
  24. string body)
  25. {
  26. Guid = guid;
  27. TreeGuid = treeGuid;
  28. ParentGuids = parentGuids;
  29. Author = author;
  30. AuthorDate = authorDate;
  31. Committer = committer;
  32. CommitDate = commitDate;
  33. Body = body;
  34. }
  35. public string Guid { get; private set; }
  36. public string TreeGuid { get; private set; }
  37. public ReadOnlyCollection<string> ParentGuids { get; private set; }
  38. public string Author { get; private set; }
  39. public DateTimeOffset AuthorDate { get; private set; }
  40. public string Committer { get; private set; }
  41. public DateTimeOffset CommitDate { get; private set; }
  42. public string Body{get; private set;}
  43. /// <summary>
  44. /// Generate header.
  45. /// </summary>
  46. /// <returns></returns>
  47. public string GetHeader()
  48. {
  49. StringBuilder header = new StringBuilder();
  50. string authorEmail = GetEmail(Author);
  51. header.AppendLine(FillToLength(HttpUtility.HtmlEncode(Strings.GetAuthorText()) + ":", COMMITHEADER_STRING_LENGTH) +
  52. "<a href='mailto:" + authorEmail + "'>" + HttpUtility.HtmlEncode(Author) + "</a>");
  53. header.AppendLine(FillToLength(HttpUtility.HtmlEncode(Strings.GetAuthorDateText()) + ":", COMMITHEADER_STRING_LENGTH) +
  54. HttpUtility.HtmlEncode(GitCommandHelpers.GetRelativeDateString(DateTime.UtcNow, AuthorDate.UtcDateTime) + " (" + AuthorDate.LocalDateTime.ToString("ddd MMM dd HH':'mm':'ss yyyy")) + ")");
  55. string committerEmail = GetEmail(Committer);
  56. header.AppendLine(FillToLength(HttpUtility.HtmlEncode(Strings.GetCommitterText()) + ":", COMMITHEADER_STRING_LENGTH) +
  57. "<a href='mailto:" + committerEmail + "'>" + HttpUtility.HtmlEncode(Committer) + "</a>");
  58. header.AppendLine(FillToLength(HttpUtility.HtmlEncode(Strings.GetCommitDateText()) + ":", COMMITHEADER_STRING_LENGTH) +
  59. HttpUtility.HtmlEncode(GitCommandHelpers.GetRelativeDateString(DateTime.UtcNow, CommitDate.UtcDateTime) + " (" + CommitDate.LocalDateTime.ToString("ddd MMM dd HH':'mm':'ss yyyy")) + ")");
  60. header.Append(FillToLength(HttpUtility.HtmlEncode(Strings.GetCommitHashText()) + ":", COMMITHEADER_STRING_LENGTH) +
  61. HttpUtility.HtmlEncode(Guid));
  62. return RemoveRedundancies(header.ToString());
  63. }
  64. private string GetEmail(string author)
  65. {
  66. if (String.IsNullOrEmpty(Author))
  67. return "";
  68. int ind = Author.IndexOf("<") + 1;
  69. if (ind == -1)
  70. return "";
  71. return Author.Substring(ind, Author.LastIndexOf(">") - ind);
  72. }
  73. /// <summary>
  74. /// Gets the commit info.
  75. /// </summary>
  76. public static CommitData GetCommitData(string sha1, ref string error)
  77. {
  78. return GetCommitData(Settings.Module, sha1, ref error);
  79. }
  80. /// <summary>
  81. /// Gets the commit info for submodule.
  82. /// </summary>
  83. public static CommitData GetCommitData(GitModule module, string sha1, ref string error)
  84. {
  85. if (module == null)
  86. throw new ArgumentNullException("module");
  87. if (sha1 == null)
  88. throw new ArgumentNullException("sha1");
  89. //Do not cache this command, since notes can be added
  90. string arguments = string.Format(CultureInfo.InvariantCulture,
  91. "log -1 --pretty=\"format:"+LogFormat+"\" {0}", sha1);
  92. var info =
  93. module.RunCmd(
  94. Settings.GitCommand,
  95. arguments,
  96. Settings.LosslessEncoding
  97. );
  98. if (info.Trim().StartsWith("fatal"))
  99. {
  100. error = "Cannot find commit " + sha1;
  101. return null;
  102. }
  103. int index = info.IndexOf(sha1) + sha1.Length;
  104. if (index < 0)
  105. {
  106. error = "Cannot find commit " + sha1;
  107. return null;
  108. }
  109. if (index >= info.Length)
  110. {
  111. error = info;
  112. return null;
  113. }
  114. CommitData commitInformation = CreateFromFormatedData(info);
  115. return commitInformation;
  116. }
  117. public const string LogFormat = "%H%n%T%n%P%n%aN <%aE>%n%at%n%cN <%cE>%n%ct%n%e%n%B%nNotes:%n%-N";
  118. /// <summary>
  119. /// Creates a CommitData object from formated commit info data from git. The string passed in should be
  120. /// exact output of a log or show command using --format=LogFormat.
  121. /// </summary>
  122. /// <param name="data">Formated commit data from git.</param>
  123. /// <returns>CommitData object populated with parsed info from git string.</returns>
  124. public static CommitData CreateFromFormatedData(string data)
  125. {
  126. if (data == null)
  127. throw new ArgumentNullException("Data");
  128. var lines = data.Split('\n');
  129. var guid = lines[0];
  130. // TODO: we can use this to add more relationship info like gitk does if wanted
  131. var treeGuid = lines[1];
  132. // TODO: we can use this to add more relationship info like gitk does if wanted
  133. string[] parentLines = lines[2].Split(new char[]{' '});
  134. ReadOnlyCollection<string> parentGuids = parentLines.ToList().AsReadOnly();
  135. var author = GitCommandHelpers.ReEncodeStringFromLossless(lines[3]);
  136. var authorDate = DateTimeUtils.ParseUnixTime(lines[4]);
  137. var committer = GitCommandHelpers.ReEncodeStringFromLossless(lines[5]);
  138. var commitDate = DateTimeUtils.ParseUnixTime(lines[6]);
  139. string commitEncoding = lines[7];
  140. int startIndex = 8;
  141. int endIndex = lines.Length - 1;
  142. if (lines[endIndex] == "Notes:")
  143. endIndex--;
  144. var message = new StringBuilder();
  145. bool bNotesStart = false;
  146. for (int i = startIndex; i <= endIndex; i++)
  147. {
  148. string line = lines[i];
  149. if (bNotesStart)
  150. line = " " + line;
  151. message.AppendLine(line);
  152. if (lines[i] == "Notes:")
  153. bNotesStart = true;
  154. }
  155. //commit message is not reencoded by git when format is given
  156. var body = GitCommandHelpers.ReEncodeCommitMessage(message.ToString(), commitEncoding);
  157. var commitInformation = new CommitData(guid, treeGuid, parentGuids, author, authorDate,
  158. committer, commitDate, body);
  159. return commitInformation;
  160. }
  161. private static string FillToLength(string input, int length)
  162. {
  163. return FillToLength(input, length, 0);
  164. }
  165. private static string FillToLength(string input, int length, int skip)
  166. {
  167. // length
  168. const int tabsize = 8;
  169. if ((input.Length - skip) < length)
  170. {
  171. int l = length - (input.Length - skip);
  172. return input + new string('\t', (l / tabsize) + ((l % tabsize) == 0 ? 0 : 1));
  173. }
  174. return input;
  175. }
  176. private static string RemoveRedundancies(string info)
  177. {
  178. string author = GetField(info, Strings.GetAuthorText() + ":");
  179. string committer = GetField(info, Strings.GetCommitterText() + ":");
  180. if (String.Equals(author, committer, StringComparison.CurrentCulture))
  181. {
  182. info = RemoveField(info, Strings.GetCommitterText() + ":");
  183. }
  184. string authorDate = GetField(info, Strings.GetAuthorDateText() + ":");
  185. string commitDate = GetField(info, Strings.GetCommitDateText() + ":");
  186. if (String.Equals(authorDate, commitDate, StringComparison.CurrentCulture))
  187. {
  188. info =
  189. RemoveField(info, Strings.GetCommitDateText() + ":").Replace(
  190. FillToLength(Strings.GetAuthorDateText() + ":", COMMITHEADER_STRING_LENGTH), FillToLength(Strings.GetDateText() + ":", COMMITHEADER_STRING_LENGTH));
  191. }
  192. return info;
  193. }
  194. private static string RemoveField(string data, string header)
  195. {
  196. int headerIndex = data.IndexOf(header);
  197. if (headerIndex == -1)
  198. return data;
  199. int endIndex = data.IndexOf('\n', headerIndex);
  200. if (endIndex == -1)
  201. endIndex = data.Length - 1;
  202. int length = endIndex - headerIndex + 1;
  203. return data.Remove(headerIndex, length);
  204. }
  205. private static string GetField(string data, string header)
  206. {
  207. int valueIndex = IndexOfValue(data, header);
  208. if (valueIndex == -1)
  209. return null;
  210. int length = LengthOfValue(data, valueIndex);
  211. return data.Substring(valueIndex, length);
  212. }
  213. private static int LengthOfValue(string data, int valueIndex)
  214. {
  215. if (valueIndex == -1)
  216. return 0;
  217. int endIndex = data.IndexOf('\n', valueIndex);
  218. if (endIndex == -1)
  219. endIndex = data.Length - 1;
  220. return endIndex - valueIndex;
  221. }
  222. private static int IndexOfValue(string data, string header)
  223. {
  224. int headerIndex = data.IndexOf(header);
  225. if (headerIndex == -1)
  226. return -1;
  227. int valueIndex = headerIndex + header.Length;
  228. while (data[valueIndex] == '\t')
  229. {
  230. valueIndex++;
  231. if (valueIndex == data.Length)
  232. return -1;
  233. }
  234. return valueIndex;
  235. }
  236. }
  237. }