1 module diff; 2 3 /** Note: Colors were hardcoded to Posix, they're disabled in the D samples. */ 4 5 import git; 6 7 import std.array; 8 import std.conv; 9 import std.stdio; 10 import std.string; 11 import std.range; 12 import std.exception; 13 14 15 GitTree resolve_to_tree(GitRepo repo, string identifier) 16 { 17 auto obj = repo.revparseSingle(identifier); 18 19 switch (obj.type) 20 { 21 default: throw Exception("Tree not found: "~identifier); 22 case GitType.tree: return obj.toTree(); 23 case GitType.commit: return obj.toCommit().tree; 24 } 25 } 26 27 extern(C) int printer( 28 const(git_diff_delta)* delta, 29 const(git_diff_range)* range, 30 char usage, 31 const(char)* line, 32 size_t line_len, 33 void* data) 34 { 35 printf("%s", line); 36 stdout.flush(); 37 return 0; 38 } 39 40 int check_uint16_param(string arg, string pattern, ushort* val) 41 { 42 arg.popFrontN(pattern.length); 43 44 try 45 { 46 *val = to!ushort(arg); 47 } 48 catch (Exception exc) 49 { 50 return 0; 51 } 52 53 return 1; 54 } 55 56 int check_str_param(string arg, string pattern, const(char)** val) 57 { 58 arg.popFrontN(pattern.length); 59 60 try 61 { 62 *val = cast(char*)toStringz(arg); 63 } 64 catch (Exception exc) 65 { 66 return 0; 67 } 68 69 return 1; 70 } 71 72 void usage(string message, string arg) 73 { 74 if (!message.empty && !arg.empty) 75 writefln("%s: %s\n", message, arg); 76 else if (!message.empty) 77 writeln(message); 78 79 assert(0, "usage: diff [<tree-oid> [<tree-oid>]]\n"); 80 } 81 82 bool strcmp(string lhs, string rhs) { return lhs != rhs; } 83 84 enum { 85 FORMAT_PATCH = 0, 86 FORMAT_COMPACT = 1, 87 FORMAT_RAW = 2 88 }; 89 90 int main(string[] args) 91 { 92 args.popFront(); 93 if (args.length < 3) 94 { 95 writeln("Must pass 3 args: Path to .git dir, and two commit hashes for the diff"); 96 return 0; 97 } 98 99 string path = args.front; 100 git_diff_options opts = GIT_DIFF_OPTIONS_INIT; 101 git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; 102 int format = FORMAT_PATCH; 103 104 int i; 105 int color = -1; 106 int compact = 0; 107 int cached = 0; 108 string dir = args[0]; 109 110 string treeish1; 111 string treeish2; 112 113 /* parse arguments as copied from git-diff */ 114 foreach (arg; args[1..$]) 115 { 116 if (arg[0] != '-') 117 { 118 if (treeish1 == null) 119 treeish1 = arg; 120 else if (treeish2 == null) 121 treeish2 = arg; 122 else 123 usage("Only one or two tree identifiers can be provided", null); 124 } 125 else if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")) 126 compact = 0; 127 else if (!strcmp(arg, "--cached")) 128 cached = 1; 129 else if (!strcmp(arg, "--name-status")) 130 compact = 1; 131 else if (!strcmp(arg, "--color")) 132 color = 0; 133 else if (!strcmp(arg, "--no-color")) 134 color = -1; 135 else if (!strcmp(arg, "-R")) 136 opts.flags |= GIT_DIFF_REVERSE; 137 else if (!strcmp(arg, "-arg") || !strcmp(arg, "--text")) 138 opts.flags |= GIT_DIFF_FORCE_TEXT; 139 else if (!strcmp(arg, "--ignore-space-at-eol")) 140 opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL; 141 else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) 142 opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE; 143 else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) 144 opts.flags |= GIT_DIFF_IGNORE_WHITESPACE; 145 else if (!strcmp(arg, "--ignored")) 146 opts.flags |= GIT_DIFF_INCLUDE_IGNORED; 147 else if (!strcmp(arg, "--untracked")) 148 opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; 149 else if (!check_uint16_param(arg, "-U", &opts.context_lines) && 150 !check_uint16_param(arg, "--unified=", &opts.context_lines) && 151 !check_uint16_param(arg, "--inter-hunk-context=", &opts.interhunk_lines) && 152 !check_str_param(arg, "--src-prefix=", &opts.old_prefix) && 153 !check_str_param(arg, "--dst-prefix=", &opts.new_prefix)) 154 usage("Unknown arg", arg); 155 } 156 157 /* open repo */ 158 GitRepo repo = openRepositoryExt(dir, GitRepositoryOpenFlags.none, null); 159 160 GitTree t1, t2; 161 if (!treeish1.empty) t1 = repo.resolteToTree(treeish1); 162 if (!treeish2.empty) t2 = repo.resolteToTree(treeish2); 163 164 /* <sha1> <sha2> */ 165 /* <sha1> --cached */ 166 /* <sha1> */ 167 /* --cached */ 168 /* nothing */ 169 170 GitDiffList diff; 171 if (t1 && t2) diff = repo.diffTreeToTree(t1, t2, &opts); 172 else if (t1 && cached) diff = repo.diffTreeToIndex(t1, null, &opts); 173 else if (t1) { 174 GitTreeList diff2; 175 diff = repo.diffTreeToIndex(t1, null, &opts); 176 diff2 = repo.diffIndexToWorkDir(null, &opts); 177 diff.merge(diff2); 178 } else if (cached) { 179 t1 = repo.resolteToTree("HEAD"); 180 diff = repo.diffTreeToIndex(t1, null, &opts); 181 } else diff = repo.diffIndexToWorkDir(null, &opts); 182 183 if ((findopts.flags & git_diff_find_t.GIT_DIFF_FIND_ALL) != 0) 184 check(git_diff_find_similar(diff, &findopts), 185 "finding renames and copies "); 186 187 switch (format) { 188 case FORMAT_PATCH: 189 check(git_diff_print_patch(diff, &printer, &color), "Displaying diff"); 190 break; 191 case FORMAT_COMPACT: 192 check(git_diff_print_compact(diff, &printer, &color), "Displaying diff"); 193 break; 194 case FORMAT_RAW: 195 check(git_diff_print_raw(diff, &printer, &color), "Displaying diff"); 196 break; 197 default: 198 } 199 200 git_diff_list_free(diff); 201 git_tree_free(t1); 202 git_tree_free(t2); 203 git_repository_free(repo); 204 205 git_threads_shutdown(); 206 207 return 0; 208 }