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