rugged 0.17.0.b6 → 0.17.0.b7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/README.md +3 -3
  2. data/Rakefile +3 -1
  3. data/ext/rugged/rugged.c +30 -0
  4. data/ext/rugged/rugged.h +9 -0
  5. data/ext/rugged/rugged_branch.c +306 -0
  6. data/ext/rugged/rugged_config.c +16 -13
  7. data/ext/rugged/rugged_index.c +25 -0
  8. data/ext/rugged/rugged_object.c +6 -2
  9. data/ext/rugged/rugged_reference.c +11 -18
  10. data/ext/rugged/rugged_revwalk.c +1 -1
  11. data/lib/rugged.rb +1 -0
  12. data/lib/rugged/branch.rb +28 -0
  13. data/lib/rugged/commit.rb +5 -5
  14. data/lib/rugged/repository.rb +32 -7
  15. data/lib/rugged/tag.rb +5 -1
  16. data/lib/rugged/version.rb +1 -1
  17. data/test/branch_test.rb +227 -0
  18. data/test/config_test.rb +1 -1
  19. data/test/fixtures/testrepo.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
  20. data/test/fixtures/testrepo.git/objects/a3/e05719b428a2d0ed7a55c4ce53dcc5768c6d5e +0 -0
  21. data/test/index_test.rb +31 -0
  22. data/test/index_test.rb~ +218 -0
  23. data/test/lib_test.rb +22 -0
  24. data/test/reference_test.rb +5 -3
  25. data/vendor/libgit2/Makefile.embed +1 -1
  26. data/vendor/libgit2/include/git2.h +1 -0
  27. data/vendor/libgit2/include/git2/branch.h +17 -13
  28. data/vendor/libgit2/include/git2/checkout.h +83 -22
  29. data/vendor/libgit2/include/git2/clone.h +6 -3
  30. data/vendor/libgit2/include/git2/common.h +1 -8
  31. data/vendor/libgit2/include/git2/config.h +185 -26
  32. data/vendor/libgit2/include/git2/diff.h +229 -17
  33. data/vendor/libgit2/include/git2/errors.h +39 -1
  34. data/vendor/libgit2/include/git2/ignore.h +6 -3
  35. data/vendor/libgit2/include/git2/indexer.h +1 -0
  36. data/vendor/libgit2/include/git2/merge.h +1 -1
  37. data/vendor/libgit2/include/git2/object.h +7 -4
  38. data/vendor/libgit2/include/git2/odb.h +4 -2
  39. data/vendor/libgit2/include/git2/odb_backend.h +6 -0
  40. data/vendor/libgit2/include/git2/oid.h +2 -0
  41. data/vendor/libgit2/include/git2/pack.h +89 -0
  42. data/vendor/libgit2/include/git2/refs.h +88 -0
  43. data/vendor/libgit2/include/git2/refspec.h +0 -8
  44. data/vendor/libgit2/include/git2/remote.h +34 -1
  45. data/vendor/libgit2/include/git2/repository.h +238 -6
  46. data/vendor/libgit2/include/git2/reset.h +4 -1
  47. data/vendor/libgit2/include/git2/revwalk.h +1 -1
  48. data/vendor/libgit2/include/git2/status.h +19 -14
  49. data/vendor/libgit2/include/git2/strarray.h +54 -0
  50. data/vendor/libgit2/include/git2/submodule.h +451 -45
  51. data/vendor/libgit2/include/git2/tag.h +16 -0
  52. data/vendor/libgit2/include/git2/tree.h +2 -2
  53. data/vendor/libgit2/include/git2/types.h +4 -0
  54. data/vendor/libgit2/src/amiga/map.c +4 -7
  55. data/vendor/libgit2/src/attr.c +21 -13
  56. data/vendor/libgit2/src/attr.h +3 -1
  57. data/vendor/libgit2/src/attr_file.c +14 -14
  58. data/vendor/libgit2/src/attr_file.h +6 -5
  59. data/vendor/libgit2/src/blob.c +22 -12
  60. data/vendor/libgit2/src/branch.c +62 -66
  61. data/vendor/libgit2/src/buffer.c +63 -14
  62. data/vendor/libgit2/src/buffer.h +4 -0
  63. data/vendor/libgit2/src/cache.c +5 -4
  64. data/vendor/libgit2/src/checkout.c +381 -159
  65. data/vendor/libgit2/src/clone.c +221 -94
  66. data/vendor/libgit2/src/common.h +13 -3
  67. data/vendor/libgit2/src/compress.c +53 -0
  68. data/vendor/libgit2/src/compress.h +16 -0
  69. data/vendor/libgit2/src/config.c +380 -175
  70. data/vendor/libgit2/src/config.h +2 -5
  71. data/vendor/libgit2/src/config_file.c +63 -46
  72. data/vendor/libgit2/src/config_file.h +16 -4
  73. data/vendor/libgit2/src/crlf.c +4 -3
  74. data/vendor/libgit2/src/delta.c +491 -0
  75. data/vendor/libgit2/src/delta.h +112 -0
  76. data/vendor/libgit2/src/diff.c +310 -67
  77. data/vendor/libgit2/src/diff.h +10 -1
  78. data/vendor/libgit2/src/diff_output.c +1030 -337
  79. data/vendor/libgit2/src/diff_output.h +86 -0
  80. data/vendor/libgit2/src/errors.c +10 -1
  81. data/vendor/libgit2/src/fetch.c +108 -24
  82. data/vendor/libgit2/src/filebuf.c +8 -2
  83. data/vendor/libgit2/src/fileops.c +342 -177
  84. data/vendor/libgit2/src/fileops.h +84 -7
  85. data/vendor/libgit2/src/filter.c +0 -35
  86. data/vendor/libgit2/src/filter.h +0 -12
  87. data/vendor/libgit2/src/{compat/fnmatch.c → fnmatch.c} +16 -4
  88. data/vendor/libgit2/src/{compat/fnmatch.h → fnmatch.h} +4 -3
  89. data/vendor/libgit2/src/global.c +4 -0
  90. data/vendor/libgit2/src/ignore.c +122 -23
  91. data/vendor/libgit2/src/ignore.h +1 -0
  92. data/vendor/libgit2/src/index.c +56 -10
  93. data/vendor/libgit2/src/index.h +2 -0
  94. data/vendor/libgit2/src/indexer.c +8 -9
  95. data/vendor/libgit2/src/iterator.c +244 -31
  96. data/vendor/libgit2/src/iterator.h +30 -1
  97. data/vendor/libgit2/src/message.c +1 -1
  98. data/vendor/libgit2/src/netops.c +44 -4
  99. data/vendor/libgit2/src/object.c +80 -69
  100. data/vendor/libgit2/src/object.h +39 -0
  101. data/vendor/libgit2/src/odb.c +79 -15
  102. data/vendor/libgit2/src/odb.h +20 -5
  103. data/vendor/libgit2/src/odb_pack.c +65 -33
  104. data/vendor/libgit2/src/oid.c +0 -3
  105. data/vendor/libgit2/src/pack-objects.c +1315 -0
  106. data/vendor/libgit2/src/pack-objects.h +87 -0
  107. data/vendor/libgit2/src/pack.c +36 -12
  108. data/vendor/libgit2/src/pack.h +1 -0
  109. data/vendor/libgit2/src/path.c +42 -9
  110. data/vendor/libgit2/src/path.h +14 -0
  111. data/vendor/libgit2/src/pkt.c +52 -2
  112. data/vendor/libgit2/src/pkt.h +10 -0
  113. data/vendor/libgit2/src/pool.h +11 -0
  114. data/vendor/libgit2/src/posix.h +8 -0
  115. data/vendor/libgit2/src/protocol.c +24 -2
  116. data/vendor/libgit2/src/protocol.h +4 -0
  117. data/vendor/libgit2/src/reflog.c +1 -1
  118. data/vendor/libgit2/src/refs.c +292 -124
  119. data/vendor/libgit2/src/refs.h +4 -2
  120. data/vendor/libgit2/src/refspec.c +117 -19
  121. data/vendor/libgit2/src/refspec.h +19 -0
  122. data/vendor/libgit2/src/remote.c +152 -48
  123. data/vendor/libgit2/src/remote.h +4 -1
  124. data/vendor/libgit2/src/repo_template.h +58 -0
  125. data/vendor/libgit2/src/repository.c +594 -179
  126. data/vendor/libgit2/src/repository.h +23 -22
  127. data/vendor/libgit2/src/reset.c +71 -29
  128. data/vendor/libgit2/src/revparse.c +26 -17
  129. data/vendor/libgit2/src/revwalk.c +36 -19
  130. data/vendor/libgit2/src/sha1.h +7 -0
  131. data/vendor/libgit2/src/{sha1.c → sha1/sha1.c} +0 -0
  132. data/vendor/libgit2/src/signature.c +12 -10
  133. data/vendor/libgit2/src/status.c +52 -6
  134. data/vendor/libgit2/src/submodule.c +1363 -255
  135. data/vendor/libgit2/src/submodule.h +102 -0
  136. data/vendor/libgit2/src/tag.c +42 -26
  137. data/vendor/libgit2/src/thread-utils.h +7 -7
  138. data/vendor/libgit2/src/transport.h +15 -1
  139. data/vendor/libgit2/src/transports/git.c +1 -1
  140. data/vendor/libgit2/src/transports/http.c +197 -36
  141. data/vendor/libgit2/src/tree.c +3 -3
  142. data/vendor/libgit2/src/unix/map.c +2 -0
  143. data/vendor/libgit2/src/unix/posix.h +1 -8
  144. data/vendor/libgit2/src/util.c +6 -1
  145. data/vendor/libgit2/src/util.h +7 -0
  146. data/vendor/libgit2/src/vector.c +16 -0
  147. data/vendor/libgit2/src/vector.h +1 -0
  148. data/vendor/libgit2/src/win32/dir.c +8 -21
  149. data/vendor/libgit2/src/win32/findfile.c +149 -0
  150. data/vendor/libgit2/src/win32/findfile.h +23 -0
  151. data/vendor/libgit2/src/win32/posix.h +3 -7
  152. data/vendor/libgit2/src/win32/posix_w32.c +44 -102
  153. data/vendor/libgit2/src/win32/pthread.c +68 -0
  154. data/vendor/libgit2/src/win32/pthread.h +7 -0
  155. data/vendor/libgit2/src/win32/utf-conv.c +60 -71
  156. data/vendor/libgit2/src/win32/utf-conv.h +4 -3
  157. metadata +70 -71
  158. data/vendor/libgit2/include/git2/windows.h +0 -59
@@ -7,6 +7,9 @@
7
7
  #ifndef INCLUDE_diff_h__
8
8
  #define INCLUDE_diff_h__
9
9
 
10
+ #include "git2/diff.h"
11
+ #include "git2/oid.h"
12
+
10
13
  #include <stdio.h>
11
14
  #include "vector.h"
12
15
  #include "buffer.h"
@@ -26,10 +29,11 @@ enum {
26
29
  };
27
30
 
28
31
  struct git_diff_list {
32
+ git_refcount rc;
29
33
  git_repository *repo;
30
34
  git_diff_options opts;
31
35
  git_vector pathspec;
32
- git_vector deltas; /* vector of git_diff_file_delta */
36
+ git_vector deltas; /* vector of git_diff_delta */
33
37
  git_pool pool;
34
38
  git_iterator_type_t old_src;
35
39
  git_iterator_type_t new_src;
@@ -39,5 +43,10 @@ struct git_diff_list {
39
43
  extern void git_diff__cleanup_modes(
40
44
  uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
41
45
 
46
+ extern void git_diff_list_addref(git_diff_list *diff);
47
+
48
+ extern bool git_diff_delta__should_skip(
49
+ const git_diff_options *opts, const git_diff_delta *delta);
50
+
42
51
  #endif
43
52
 
@@ -5,28 +5,14 @@
5
5
  * a Linking Exception. For full terms see the included COPYING file.
6
6
  */
7
7
  #include "common.h"
8
- #include "git2/diff.h"
9
8
  #include "git2/attr.h"
10
- #include "git2/blob.h"
11
9
  #include "git2/oid.h"
12
- #include "xdiff/xdiff.h"
10
+ #include "git2/submodule.h"
11
+ #include "diff_output.h"
13
12
  #include <ctype.h>
14
- #include "diff.h"
15
- #include "map.h"
16
13
  #include "fileops.h"
17
14
  #include "filter.h"
18
15
 
19
- typedef struct {
20
- git_diff_list *diff;
21
- void *cb_data;
22
- git_diff_hunk_fn hunk_cb;
23
- git_diff_data_fn line_cb;
24
- unsigned int index;
25
- git_diff_delta *delta;
26
- git_diff_range range;
27
- int error;
28
- } diff_output_info;
29
-
30
16
  static int read_next_int(const char **str, int *value)
31
17
  {
32
18
  const char *scan = *str;
@@ -41,71 +27,43 @@ static int read_next_int(const char **str, int *value)
41
27
  return (digits > 0) ? 0 : -1;
42
28
  }
43
29
 
44
- static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
45
- {
46
- diff_output_info *info = priv;
47
-
48
- if (len == 1 && info->hunk_cb) {
49
- git_diff_range range = { -1, 0, -1, 0 };
50
- const char *scan = bufs[0].ptr;
51
-
52
- /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
53
- if (*scan != '@')
54
- info->error = -1;
55
- else if (read_next_int(&scan, &range.old_start) < 0)
56
- info->error = -1;
57
- else if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0)
58
- info->error = -1;
59
- else if (read_next_int(&scan, &range.new_start) < 0)
60
- info->error = -1;
61
- else if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)
62
- info->error = -1;
63
- else if (range.old_start < 0 || range.new_start < 0)
64
- info->error = -1;
65
- else {
66
- memcpy(&info->range, &range, sizeof(git_diff_range));
67
-
68
- if (info->hunk_cb(
69
- info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size))
70
- info->error = GIT_EUSER;
71
- }
72
- }
73
-
74
- if ((len == 2 || len == 3) && info->line_cb) {
75
- int origin;
76
-
77
- /* expect " "/"-"/"+", then data, then maybe newline */
78
- origin =
79
- (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
80
- (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
81
- GIT_DIFF_LINE_CONTEXT;
82
-
83
- if (info->line_cb(
84
- info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size))
85
- info->error = GIT_EUSER;
86
-
87
- /* This should only happen if we are adding a line that does not
88
- * have a newline at the end and the old code did. In that case,
89
- * we have a ADD with a DEL_EOFNL as a pair.
90
- */
91
- else if (len == 3) {
92
- origin = (origin == GIT_DIFF_LINE_ADDITION) ?
93
- GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL;
94
-
95
- if (info->line_cb(
96
- info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size))
97
- info->error = GIT_EUSER;
98
- }
99
- }
30
+ static int parse_hunk_header(git_diff_range *range, const char *header)
31
+ {
32
+ /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
33
+ if (*header != '@')
34
+ return -1;
35
+ if (read_next_int(&header, &range->old_start) < 0)
36
+ return -1;
37
+ if (*header == ',') {
38
+ if (read_next_int(&header, &range->old_lines) < 0)
39
+ return -1;
40
+ } else
41
+ range->old_lines = 1;
42
+ if (read_next_int(&header, &range->new_start) < 0)
43
+ return -1;
44
+ if (*header == ',') {
45
+ if (read_next_int(&header, &range->new_lines) < 0)
46
+ return -1;
47
+ } else
48
+ range->new_lines = 1;
49
+ if (range->old_start < 0 || range->new_start < 0)
50
+ return -1;
100
51
 
101
- return info->error;
52
+ return 0;
102
53
  }
103
54
 
104
- #define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
55
+ #define KNOWN_BINARY_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
56
+ #define NOT_BINARY_FLAGS (GIT_DIFF_FILE_NOT_BINARY|GIT_DIFF_FILE_NO_DATA)
105
57
 
106
- static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
58
+ static int update_file_is_binary_by_attr(
59
+ git_repository *repo, git_diff_file *file)
107
60
  {
108
61
  const char *value;
62
+
63
+ /* because of blob diffs, cannot assume path is set */
64
+ if (!file->path || !strlen(file->path))
65
+ return 0;
66
+
109
67
  if (git_attr_get(&value, repo, 0, file->path, "diff") < 0)
110
68
  return -1;
111
69
 
@@ -123,17 +81,19 @@ static void update_delta_is_binary(git_diff_delta *delta)
123
81
  if ((delta->old_file.flags & GIT_DIFF_FILE_BINARY) != 0 ||
124
82
  (delta->new_file.flags & GIT_DIFF_FILE_BINARY) != 0)
125
83
  delta->binary = 1;
126
- else if ((delta->old_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0 ||
127
- (delta->new_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0)
84
+
85
+ else if ((delta->old_file.flags & NOT_BINARY_FLAGS) != 0 &&
86
+ (delta->new_file.flags & NOT_BINARY_FLAGS) != 0)
128
87
  delta->binary = 0;
88
+
129
89
  /* otherwise leave delta->binary value untouched */
130
90
  }
131
91
 
132
- static int file_is_binary_by_attr(
133
- git_diff_list *diff,
134
- git_diff_delta *delta)
92
+ static int diff_delta_is_binary_by_attr(
93
+ diff_context *ctxt, git_diff_patch *patch)
135
94
  {
136
95
  int error = 0, mirror_new;
96
+ git_diff_delta *delta = patch->delta;
137
97
 
138
98
  delta->binary = -1;
139
99
 
@@ -148,7 +108,7 @@ static int file_is_binary_by_attr(
148
108
  }
149
109
 
150
110
  /* check if user is forcing us to text diff these files */
151
- if (diff->opts.flags & GIT_DIFF_FORCE_TEXT) {
111
+ if (ctxt->opts && (ctxt->opts->flags & GIT_DIFF_FORCE_TEXT) != 0) {
152
112
  delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
153
113
  delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
154
114
  delta->binary = 0;
@@ -156,57 +116,69 @@ static int file_is_binary_by_attr(
156
116
  }
157
117
 
158
118
  /* check diff attribute +, -, or 0 */
159
- if (update_file_is_binary_by_attr(diff->repo, &delta->old_file) < 0)
119
+ if (update_file_is_binary_by_attr(ctxt->repo, &delta->old_file) < 0)
160
120
  return -1;
161
121
 
162
122
  mirror_new = (delta->new_file.path == delta->old_file.path ||
163
123
  strcmp(delta->new_file.path, delta->old_file.path) == 0);
164
124
  if (mirror_new)
165
- delta->new_file.flags |= (delta->old_file.flags & BINARY_DIFF_FLAGS);
125
+ delta->new_file.flags |= (delta->old_file.flags & KNOWN_BINARY_FLAGS);
166
126
  else
167
- error = update_file_is_binary_by_attr(diff->repo, &delta->new_file);
127
+ error = update_file_is_binary_by_attr(ctxt->repo, &delta->new_file);
168
128
 
169
129
  update_delta_is_binary(delta);
170
130
 
171
131
  return error;
172
132
  }
173
133
 
174
- static int file_is_binary_by_content(
175
- git_diff_delta *delta,
176
- git_map *old_data,
177
- git_map *new_data)
134
+ static int diff_delta_is_binary_by_content(
135
+ diff_context *ctxt, git_diff_delta *delta, git_diff_file *file, git_map *map)
178
136
  {
179
137
  git_buf search;
180
138
 
181
- if ((delta->old_file.flags & BINARY_DIFF_FLAGS) == 0) {
182
- search.ptr = old_data->data;
183
- search.size = min(old_data->len, 4000);
139
+ GIT_UNUSED(ctxt);
140
+
141
+ if ((file->flags & KNOWN_BINARY_FLAGS) == 0) {
142
+ search.ptr = map->data;
143
+ search.size = min(map->len, 4000);
184
144
 
185
145
  if (git_buf_is_binary(&search))
186
- delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
146
+ file->flags |= GIT_DIFF_FILE_BINARY;
187
147
  else
188
- delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
148
+ file->flags |= GIT_DIFF_FILE_NOT_BINARY;
189
149
  }
190
150
 
191
- if ((delta->new_file.flags & BINARY_DIFF_FLAGS) == 0) {
192
- search.ptr = new_data->data;
193
- search.size = min(new_data->len, 4000);
151
+ update_delta_is_binary(delta);
194
152
 
195
- if (git_buf_is_binary(&search))
196
- delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
197
- else
198
- delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
153
+ return 0;
154
+ }
155
+
156
+ static int diff_delta_is_binary_by_size(
157
+ diff_context *ctxt, git_diff_delta *delta, git_diff_file *file)
158
+ {
159
+ git_off_t threshold = MAX_DIFF_FILESIZE;
160
+
161
+ if ((file->flags & KNOWN_BINARY_FLAGS) != 0)
162
+ return 0;
163
+
164
+ if (ctxt && ctxt->opts) {
165
+ if (ctxt->opts->max_size < 0)
166
+ return 0;
167
+
168
+ if (ctxt->opts->max_size > 0)
169
+ threshold = ctxt->opts->max_size;
199
170
  }
200
171
 
201
- update_delta_is_binary(delta);
172
+ if (file->size > threshold)
173
+ file->flags |= GIT_DIFF_FILE_BINARY;
202
174
 
203
- /* TODO: if value != NULL, implement diff drivers */
175
+ update_delta_is_binary(delta);
204
176
 
205
177
  return 0;
206
178
  }
207
179
 
208
180
  static void setup_xdiff_options(
209
- git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param)
181
+ const git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param)
210
182
  {
211
183
  memset(cfg, 0, sizeof(xdemitconf_t));
212
184
  memset(param, 0, sizeof(xpparam_t));
@@ -227,54 +199,218 @@ static void setup_xdiff_options(
227
199
  param->flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
228
200
  }
229
201
 
202
+
230
203
  static int get_blob_content(
231
- git_repository *repo,
232
- const git_oid *oid,
204
+ diff_context *ctxt,
205
+ git_diff_delta *delta,
206
+ git_diff_file *file,
233
207
  git_map *map,
234
208
  git_blob **blob)
235
209
  {
236
- if (git_oid_iszero(oid))
210
+ int error;
211
+ git_odb_object *odb_obj = NULL;
212
+
213
+ if (git_oid_iszero(&file->oid))
237
214
  return 0;
238
215
 
239
- if (git_blob_lookup(blob, repo, oid) < 0)
240
- return -1;
216
+ if (file->mode == GIT_FILEMODE_COMMIT)
217
+ {
218
+ char oidstr[GIT_OID_HEXSZ+1];
219
+ git_buf content = GIT_BUF_INIT;
220
+
221
+ git_oid_fmt(oidstr, &file->oid);
222
+ oidstr[GIT_OID_HEXSZ] = 0;
223
+ git_buf_printf(&content, "Subproject commit %s\n", oidstr );
224
+
225
+ map->data = git_buf_detach(&content);
226
+ map->len = strlen(map->data);
227
+
228
+ file->flags |= GIT_DIFF_FILE_FREE_DATA;
229
+ return 0;
230
+ }
231
+
232
+ if (!file->size) {
233
+ git_odb *odb;
234
+ size_t len;
235
+ git_otype type;
236
+
237
+ /* peek at object header to avoid loading if too large */
238
+ if ((error = git_repository_odb__weakptr(&odb, ctxt->repo)) < 0 ||
239
+ (error = git_odb__read_header_or_object(
240
+ &odb_obj, &len, &type, odb, &file->oid)) < 0)
241
+ return error;
242
+
243
+ assert(type == GIT_OBJ_BLOB);
244
+
245
+ file->size = len;
246
+ }
247
+
248
+ /* if blob is too large to diff, mark as binary */
249
+ if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0)
250
+ return error;
251
+ if (delta->binary == 1)
252
+ return 0;
253
+
254
+ if (odb_obj != NULL) {
255
+ error = git_object__from_odb_object(
256
+ (git_object **)blob, ctxt->repo, odb_obj, GIT_OBJ_BLOB);
257
+ git_odb_object_free(odb_obj);
258
+ } else
259
+ error = git_blob_lookup(blob, ctxt->repo, &file->oid);
260
+
261
+ if (error)
262
+ return error;
241
263
 
242
264
  map->data = (void *)git_blob_rawcontent(*blob);
243
265
  map->len = git_blob_rawsize(*blob);
266
+
267
+ return diff_delta_is_binary_by_content(ctxt, delta, file, map);
268
+ }
269
+
270
+ static int get_workdir_sm_content(
271
+ diff_context *ctxt,
272
+ git_diff_file *file,
273
+ git_map *map)
274
+ {
275
+ int error = 0;
276
+ git_buf content = GIT_BUF_INIT;
277
+ git_submodule* sm = NULL;
278
+ unsigned int sm_status = 0;
279
+ const char* sm_status_text = "";
280
+ char oidstr[GIT_OID_HEXSZ+1];
281
+
282
+ if ((error = git_submodule_lookup(&sm, ctxt->repo, file->path)) < 0 ||
283
+ (error = git_submodule_status(&sm_status, sm)) < 0)
284
+ return error;
285
+
286
+ /* update OID if we didn't have it previously */
287
+ if ((file->flags & GIT_DIFF_FILE_VALID_OID) == 0) {
288
+ const git_oid* sm_head;
289
+
290
+ if ((sm_head = git_submodule_wd_oid(sm)) != NULL ||
291
+ (sm_head = git_submodule_head_oid(sm)) != NULL)
292
+ {
293
+ git_oid_cpy(&file->oid, sm_head);
294
+ file->flags |= GIT_DIFF_FILE_VALID_OID;
295
+ }
296
+ }
297
+
298
+ git_oid_fmt(oidstr, &file->oid);
299
+ oidstr[GIT_OID_HEXSZ] = '\0';
300
+
301
+ if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
302
+ sm_status_text = "-dirty";
303
+
304
+ git_buf_printf(&content, "Subproject commit %s%s\n",
305
+ oidstr, sm_status_text);
306
+
307
+ map->data = git_buf_detach(&content);
308
+ map->len = strlen(map->data);
309
+
310
+ file->flags |= GIT_DIFF_FILE_FREE_DATA;
311
+
244
312
  return 0;
245
313
  }
246
314
 
247
315
  static int get_workdir_content(
248
- git_repository *repo,
316
+ diff_context *ctxt,
317
+ git_diff_delta *delta,
249
318
  git_diff_file *file,
250
319
  git_map *map)
251
320
  {
252
321
  int error = 0;
253
322
  git_buf path = GIT_BUF_INIT;
323
+ const char *wd = git_repository_workdir(ctxt->repo);
324
+
325
+ if (S_ISGITLINK(file->mode))
326
+ return get_workdir_sm_content(ctxt, file, map);
327
+
328
+ if (S_ISDIR(file->mode))
329
+ return 0;
254
330
 
255
- if (git_buf_joinpath(&path, git_repository_workdir(repo), file->path) < 0)
331
+ if (git_buf_joinpath(&path, wd, file->path) < 0)
256
332
  return -1;
257
333
 
258
334
  if (S_ISLNK(file->mode)) {
259
- ssize_t read_len;
335
+ ssize_t alloc_len, read_len;
260
336
 
261
337
  file->flags |= GIT_DIFF_FILE_FREE_DATA;
262
338
  file->flags |= GIT_DIFF_FILE_BINARY;
263
339
 
264
- map->data = git__malloc((size_t)file->size + 1);
340
+ /* link path on disk could be UTF-16, so prepare a buffer that is
341
+ * big enough to handle some UTF-8 data expansion
342
+ */
343
+ alloc_len = (ssize_t)(file->size * 2) + 1;
344
+
345
+ map->data = git__malloc(alloc_len);
265
346
  GITERR_CHECK_ALLOC(map->data);
266
347
 
267
- read_len = p_readlink(path.ptr, map->data, (size_t)file->size + 1);
268
- if (read_len != (ssize_t)file->size) {
348
+ read_len = p_readlink(path.ptr, map->data, (int)alloc_len);
349
+ if (read_len < 0) {
269
350
  giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);
270
351
  error = -1;
271
- } else
272
- map->len = read_len;
352
+ goto cleanup;
353
+ }
354
+
355
+ map->len = read_len;
273
356
  }
274
357
  else {
275
- error = git_futils_mmap_ro_file(map, path.ptr);
276
- file->flags |= GIT_DIFF_FILE_UNMAP_DATA;
358
+ git_file fd = git_futils_open_ro(path.ptr);
359
+ git_vector filters = GIT_VECTOR_INIT;
360
+
361
+ if (fd < 0) {
362
+ error = fd;
363
+ goto cleanup;
364
+ }
365
+
366
+ if (!file->size)
367
+ file->size = git_futils_filesize(fd);
368
+
369
+ if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0 ||
370
+ delta->binary == 1)
371
+ goto close_and_cleanup;
372
+
373
+ error = git_filters_load(
374
+ &filters, ctxt->repo, file->path, GIT_FILTER_TO_ODB);
375
+ if (error < 0)
376
+ goto close_and_cleanup;
377
+
378
+ if (error == 0) { /* note: git_filters_load returns filter count */
379
+ error = git_futils_mmap_ro(map, fd, 0, (size_t)file->size);
380
+ file->flags |= GIT_DIFF_FILE_UNMAP_DATA;
381
+ } else {
382
+ git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
383
+
384
+ if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)file->size)) &&
385
+ !(error = git_filters_apply(&filtered, &raw, &filters)))
386
+ {
387
+ map->len = git_buf_len(&filtered);
388
+ map->data = git_buf_detach(&filtered);
389
+
390
+ file->flags |= GIT_DIFF_FILE_FREE_DATA;
391
+ }
392
+
393
+ git_buf_free(&raw);
394
+ git_buf_free(&filtered);
395
+ }
396
+
397
+ close_and_cleanup:
398
+ git_filters_free(&filters);
399
+ p_close(fd);
400
+ }
401
+
402
+ /* once data is loaded, update OID if we didn't have it previously */
403
+ if (!error && (file->flags & GIT_DIFF_FILE_VALID_OID) == 0) {
404
+ error = git_odb_hash(
405
+ &file->oid, map->data, map->len, GIT_OBJ_BLOB);
406
+ if (!error)
407
+ file->flags |= GIT_DIFF_FILE_VALID_OID;
277
408
  }
409
+
410
+ if (!error)
411
+ error = diff_delta_is_binary_by_content(ctxt, delta, file, map);
412
+
413
+ cleanup:
278
414
  git_buf_free(&path);
279
415
  return error;
280
416
  }
@@ -286,185 +422,529 @@ static void release_content(git_diff_file *file, git_map *map, git_blob *blob)
286
422
 
287
423
  if (file->flags & GIT_DIFF_FILE_FREE_DATA) {
288
424
  git__free(map->data);
289
- map->data = NULL;
425
+ map->data = "";
426
+ map->len = 0;
290
427
  file->flags &= ~GIT_DIFF_FILE_FREE_DATA;
291
428
  }
292
429
  else if (file->flags & GIT_DIFF_FILE_UNMAP_DATA) {
293
430
  git_futils_mmap_free(map);
294
- map->data = NULL;
431
+ map->data = "";
432
+ map->len = 0;
295
433
  file->flags &= ~GIT_DIFF_FILE_UNMAP_DATA;
296
434
  }
297
435
  }
298
436
 
299
- static void fill_map_from_mmfile(git_map *dst, mmfile_t *src) {
300
- assert(dst && src);
301
-
302
- dst->data = src->ptr;
303
- dst->len = src->size;
304
- #ifdef GIT_WIN32
305
- dst->fmh = NULL;
306
- #endif
307
- }
308
437
 
309
- int git_diff_foreach(
438
+ static void diff_context_init(
439
+ diff_context *ctxt,
310
440
  git_diff_list *diff,
441
+ git_repository *repo,
442
+ const git_diff_options *opts,
311
443
  void *data,
312
444
  git_diff_file_fn file_cb,
313
445
  git_diff_hunk_fn hunk_cb,
314
- git_diff_data_fn line_cb)
446
+ git_diff_data_fn data_cb)
315
447
  {
316
- int error = 0;
317
- diff_output_info info;
318
- git_diff_delta *delta;
319
- xpparam_t xdiff_params;
320
- xdemitconf_t xdiff_config;
321
- xdemitcb_t xdiff_callback;
448
+ memset(ctxt, 0, sizeof(diff_context));
449
+
450
+ ctxt->repo = repo;
451
+ ctxt->diff = diff;
452
+ ctxt->opts = opts;
453
+ ctxt->file_cb = file_cb;
454
+ ctxt->hunk_cb = hunk_cb;
455
+ ctxt->data_cb = data_cb;
456
+ ctxt->cb_data = data;
457
+ ctxt->cb_error = 0;
458
+
459
+ setup_xdiff_options(ctxt->opts, &ctxt->xdiff_config, &ctxt->xdiff_params);
460
+ }
322
461
 
323
- memset(&info, 0, sizeof(info));
324
- info.diff = diff;
325
- info.cb_data = data;
326
- info.hunk_cb = hunk_cb;
327
- info.line_cb = line_cb;
462
+ static int diff_delta_file_callback(
463
+ diff_context *ctxt, git_diff_delta *delta, size_t idx)
464
+ {
465
+ float progress;
328
466
 
329
- setup_xdiff_options(&diff->opts, &xdiff_config, &xdiff_params);
330
- memset(&xdiff_callback, 0, sizeof(xdiff_callback));
331
- xdiff_callback.outf = diff_output_cb;
332
- xdiff_callback.priv = &info;
467
+ if (!ctxt->file_cb)
468
+ return 0;
333
469
 
334
- git_vector_foreach(&diff->deltas, info.index, delta) {
335
- git_blob *old_blob = NULL, *new_blob = NULL;
336
- git_map old_data, new_data;
337
- mmfile_t old_xdiff_data, new_xdiff_data;
470
+ progress = ctxt->diff ? ((float)idx / ctxt->diff->deltas.length) : 1.0f;
338
471
 
339
- if (delta->status == GIT_DELTA_UNMODIFIED &&
340
- (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
341
- continue;
472
+ if (ctxt->file_cb(ctxt->cb_data, delta, progress) != 0)
473
+ ctxt->cb_error = GIT_EUSER;
342
474
 
343
- if (delta->status == GIT_DELTA_IGNORED &&
344
- (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
345
- continue;
475
+ return ctxt->cb_error;
476
+ }
346
477
 
347
- if (delta->status == GIT_DELTA_UNTRACKED &&
348
- (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
349
- continue;
478
+ static void diff_patch_init(
479
+ diff_context *ctxt, git_diff_patch *patch)
480
+ {
481
+ memset(patch, 0, sizeof(git_diff_patch));
350
482
 
351
- if ((error = file_is_binary_by_attr(diff, delta)) < 0)
352
- goto cleanup;
483
+ patch->diff = ctxt->diff;
484
+ patch->ctxt = ctxt;
353
485
 
354
- old_data.data = "";
355
- old_data.len = 0;
356
- new_data.data = "";
357
- new_data.len = 0;
486
+ if (patch->diff) {
487
+ patch->old_src = patch->diff->old_src;
488
+ patch->new_src = patch->diff->new_src;
489
+ } else {
490
+ patch->old_src = patch->new_src = GIT_ITERATOR_TREE;
491
+ }
492
+ }
358
493
 
359
- /* TODO: Partial blob reading to defer loading whole blob.
360
- * I.e. I want a blob with just the first 4kb loaded, then
361
- * later on I will read the rest of the blob if needed.
362
- */
494
+ static git_diff_patch *diff_patch_alloc(
495
+ diff_context *ctxt, git_diff_delta *delta)
496
+ {
497
+ git_diff_patch *patch = git__malloc(sizeof(git_diff_patch));
498
+ if (!patch)
499
+ return NULL;
363
500
 
364
- /* map files */
365
- if (delta->binary != 1 &&
366
- (hunk_cb || line_cb || git_oid_iszero(&delta->old_file.oid)) &&
367
- (delta->status == GIT_DELTA_DELETED ||
368
- delta->status == GIT_DELTA_MODIFIED))
369
- {
370
- if (diff->old_src == GIT_ITERATOR_WORKDIR)
371
- error = get_workdir_content(diff->repo, &delta->old_file, &old_data);
372
- else
373
- error = get_blob_content(
374
- diff->repo, &delta->old_file.oid, &old_data, &old_blob);
375
-
376
- if (error < 0)
377
- goto cleanup;
378
- }
501
+ diff_patch_init(ctxt, patch);
379
502
 
380
- if (delta->binary != 1 &&
381
- (hunk_cb || line_cb || git_oid_iszero(&delta->new_file.oid)) &&
382
- (delta->status == GIT_DELTA_ADDED ||
383
- delta->status == GIT_DELTA_MODIFIED))
384
- {
385
- if (diff->new_src == GIT_ITERATOR_WORKDIR)
386
- error = get_workdir_content(diff->repo, &delta->new_file, &new_data);
387
- else
388
- error = get_blob_content(
389
- diff->repo, &delta->new_file.oid, &new_data, &new_blob);
390
-
391
- if (error < 0)
392
- goto cleanup;
393
-
394
- if ((delta->new_file.flags & GIT_DIFF_FILE_VALID_OID) == 0) {
395
- error = git_odb_hash(
396
- &delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB);
397
-
398
- if (error < 0)
399
- goto cleanup;
400
- delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
401
-
402
- /* since we did not have the definitive oid, we may have
403
- * incorrect status and need to skip this item.
404
- */
405
- if (delta->old_file.mode == delta->new_file.mode &&
406
- !git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
407
- {
408
- delta->status = GIT_DELTA_UNMODIFIED;
409
- if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
410
- goto cleanup;
411
- }
412
- }
413
- }
503
+ git_diff_list_addref(patch->diff);
414
504
 
415
- /* if we have not already decided whether file is binary,
416
- * check the first 4K for nul bytes to decide...
417
- */
418
- if (delta->binary == -1) {
419
- error = file_is_binary_by_content(
420
- delta, &old_data, &new_data);
421
- if (error < 0)
422
- goto cleanup;
423
- }
505
+ GIT_REFCOUNT_INC(patch);
424
506
 
425
- /* TODO: if ignore_whitespace is set, then we *must* do text
426
- * diffs to tell if a file has really been changed.
427
- */
507
+ patch->delta = delta;
508
+ patch->flags = GIT_DIFF_PATCH_ALLOCATED;
428
509
 
429
- if (file_cb != NULL &&
430
- file_cb(data, delta, (float)info.index / diff->deltas.length))
431
- {
432
- error = GIT_EUSER;
433
- goto cleanup;
434
- }
510
+ return patch;
511
+ }
512
+
513
+ static int diff_patch_load(
514
+ diff_context *ctxt, git_diff_patch *patch)
515
+ {
516
+ int error = 0;
517
+ git_diff_delta *delta = patch->delta;
518
+ bool check_if_unmodified = false;
519
+
520
+ if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0)
521
+ return 0;
522
+
523
+ error = diff_delta_is_binary_by_attr(ctxt, patch);
524
+
525
+ patch->old_data.data = "";
526
+ patch->old_data.len = 0;
527
+ patch->old_blob = NULL;
528
+
529
+ patch->new_data.data = "";
530
+ patch->new_data.len = 0;
531
+ patch->new_blob = NULL;
532
+
533
+ if (delta->binary == 1)
534
+ goto cleanup;
535
+
536
+ if (!ctxt->hunk_cb &&
537
+ !ctxt->data_cb &&
538
+ (ctxt->opts->flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0)
539
+ goto cleanup;
540
+
541
+ switch (delta->status) {
542
+ case GIT_DELTA_ADDED:
543
+ delta->old_file.flags |= GIT_DIFF_FILE_NO_DATA;
544
+ break;
545
+ case GIT_DELTA_DELETED:
546
+ delta->new_file.flags |= GIT_DIFF_FILE_NO_DATA;
547
+ break;
548
+ case GIT_DELTA_MODIFIED:
549
+ break;
550
+ case GIT_DELTA_UNTRACKED:
551
+ delta->old_file.flags |= GIT_DIFF_FILE_NO_DATA;
552
+ if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)
553
+ delta->new_file.flags |= GIT_DIFF_FILE_NO_DATA;
554
+ break;
555
+ default:
556
+ delta->new_file.flags |= GIT_DIFF_FILE_NO_DATA;
557
+ delta->old_file.flags |= GIT_DIFF_FILE_NO_DATA;
558
+ break;
559
+ }
560
+
561
+ #define CHECK_UNMODIFIED (GIT_DIFF_FILE_NO_DATA | GIT_DIFF_FILE_VALID_OID)
435
562
 
436
- /* don't do hunk and line diffs if file is binary */
563
+ check_if_unmodified =
564
+ (delta->old_file.flags & CHECK_UNMODIFIED) == 0 &&
565
+ (delta->new_file.flags & CHECK_UNMODIFIED) == 0;
566
+
567
+ /* Always try to load workdir content first, since it may need to be
568
+ * filtered (and hence use 2x memory) and we want to minimize the max
569
+ * memory footprint during diff.
570
+ */
571
+
572
+ if ((delta->old_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 &&
573
+ patch->old_src == GIT_ITERATOR_WORKDIR) {
574
+ if ((error = get_workdir_content(
575
+ ctxt, delta, &delta->old_file, &patch->old_data)) < 0)
576
+ goto cleanup;
437
577
  if (delta->binary == 1)
438
578
  goto cleanup;
579
+ }
439
580
 
440
- /* nothing to do if we did not get data */
441
- if (!old_data.len && !new_data.len)
581
+ if ((delta->new_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 &&
582
+ patch->new_src == GIT_ITERATOR_WORKDIR) {
583
+ if ((error = get_workdir_content(
584
+ ctxt, delta, &delta->new_file, &patch->new_data)) < 0)
442
585
  goto cleanup;
586
+ if (delta->binary == 1)
587
+ goto cleanup;
588
+ }
443
589
 
444
- /* nothing to do if only diff was a mode change */
445
- if (!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
590
+ if ((delta->old_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 &&
591
+ patch->old_src != GIT_ITERATOR_WORKDIR) {
592
+ if ((error = get_blob_content(
593
+ ctxt, delta, &delta->old_file,
594
+ &patch->old_data, &patch->old_blob)) < 0)
446
595
  goto cleanup;
596
+ if (delta->binary == 1)
597
+ goto cleanup;
598
+ }
447
599
 
448
- assert(hunk_cb || line_cb);
600
+ if ((delta->new_file.flags & GIT_DIFF_FILE_NO_DATA) == 0 &&
601
+ patch->new_src != GIT_ITERATOR_WORKDIR) {
602
+ if ((error = get_blob_content(
603
+ ctxt, delta, &delta->new_file,
604
+ &patch->new_data, &patch->new_blob)) < 0)
605
+ goto cleanup;
606
+ if (delta->binary == 1)
607
+ goto cleanup;
608
+ }
449
609
 
450
- info.delta = delta;
451
- old_xdiff_data.ptr = old_data.data;
452
- old_xdiff_data.size = old_data.len;
453
- new_xdiff_data.ptr = new_data.data;
454
- new_xdiff_data.size = new_data.len;
610
+ /* if we did not previously have the definitive oid, we may have
611
+ * incorrect status and need to switch this to UNMODIFIED.
612
+ */
613
+ if (check_if_unmodified &&
614
+ delta->old_file.mode == delta->new_file.mode &&
615
+ !git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
616
+ {
617
+ delta->status = GIT_DELTA_UNMODIFIED;
455
618
 
456
- xdl_diff(&old_xdiff_data, &new_xdiff_data,
457
- &xdiff_params, &xdiff_config, &xdiff_callback);
458
- error = info.error;
619
+ if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
620
+ goto cleanup;
621
+ }
459
622
 
460
623
  cleanup:
461
- release_content(&delta->old_file, &old_data, old_blob);
462
- release_content(&delta->new_file, &new_data, new_blob);
624
+ if (delta->binary == -1)
625
+ update_delta_is_binary(delta);
626
+
627
+ if (!error) {
628
+ patch->flags |= GIT_DIFF_PATCH_LOADED;
629
+
630
+ if (delta->binary != 1 &&
631
+ delta->status != GIT_DELTA_UNMODIFIED &&
632
+ (patch->old_data.len || patch->new_data.len) &&
633
+ !git_oid_equal(&delta->old_file.oid, &delta->new_file.oid))
634
+ patch->flags |= GIT_DIFF_PATCH_DIFFABLE;
635
+ }
636
+
637
+ return error;
638
+ }
639
+
640
+ static int diff_patch_cb(void *priv, mmbuffer_t *bufs, int len)
641
+ {
642
+ git_diff_patch *patch = priv;
643
+ diff_context *ctxt = patch->ctxt;
644
+
645
+ if (len == 1) {
646
+ ctxt->cb_error = parse_hunk_header(&ctxt->cb_range, bufs[0].ptr);
647
+ if (ctxt->cb_error < 0)
648
+ return ctxt->cb_error;
649
+
650
+ if (ctxt->hunk_cb != NULL &&
651
+ ctxt->hunk_cb(ctxt->cb_data, patch->delta, &ctxt->cb_range,
652
+ bufs[0].ptr, bufs[0].size))
653
+ ctxt->cb_error = GIT_EUSER;
654
+ }
655
+
656
+ if (len == 2 || len == 3) {
657
+ /* expect " "/"-"/"+", then data */
658
+ char origin =
659
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
660
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
661
+ GIT_DIFF_LINE_CONTEXT;
662
+
663
+ if (ctxt->data_cb != NULL &&
664
+ ctxt->data_cb(ctxt->cb_data, patch->delta, &ctxt->cb_range,
665
+ origin, bufs[1].ptr, bufs[1].size))
666
+ ctxt->cb_error = GIT_EUSER;
667
+ }
668
+
669
+ if (len == 3 && !ctxt->cb_error) {
670
+ /* If we have a '+' and a third buf, then we have added a line
671
+ * without a newline and the old code had one, so DEL_EOFNL.
672
+ * If we have a '-' and a third buf, then we have removed a line
673
+ * with out a newline but added a blank line, so ADD_EOFNL.
674
+ */
675
+ char origin =
676
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
677
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
678
+ GIT_DIFF_LINE_CONTEXT;
679
+
680
+ if (ctxt->data_cb != NULL &&
681
+ ctxt->data_cb(ctxt->cb_data, patch->delta, &ctxt->cb_range,
682
+ origin, bufs[2].ptr, bufs[2].size))
683
+ ctxt->cb_error = GIT_EUSER;
684
+ }
685
+
686
+ return ctxt->cb_error;
687
+ }
688
+
689
+ static int diff_patch_generate(
690
+ diff_context *ctxt, git_diff_patch *patch)
691
+ {
692
+ int error = 0;
693
+ xdemitcb_t xdiff_callback;
694
+ mmfile_t old_xdiff_data, new_xdiff_data;
695
+
696
+ if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
697
+ return 0;
698
+
699
+ if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0)
700
+ if ((error = diff_patch_load(ctxt, patch)) < 0)
701
+ return error;
702
+
703
+ if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0)
704
+ return 0;
705
+
706
+ if (!ctxt->file_cb && !ctxt->hunk_cb)
707
+ return 0;
708
+
709
+ patch->ctxt = ctxt;
710
+
711
+ memset(&xdiff_callback, 0, sizeof(xdiff_callback));
712
+ xdiff_callback.outf = diff_patch_cb;
713
+ xdiff_callback.priv = patch;
714
+
715
+ old_xdiff_data.ptr = patch->old_data.data;
716
+ old_xdiff_data.size = patch->old_data.len;
717
+ new_xdiff_data.ptr = patch->new_data.data;
718
+ new_xdiff_data.size = patch->new_data.len;
719
+
720
+ xdl_diff(&old_xdiff_data, &new_xdiff_data,
721
+ &ctxt->xdiff_params, &ctxt->xdiff_config, &xdiff_callback);
722
+
723
+ error = ctxt->cb_error;
724
+
725
+ if (!error)
726
+ patch->flags |= GIT_DIFF_PATCH_DIFFED;
727
+
728
+ return error;
729
+ }
730
+
731
+ static void diff_patch_unload(git_diff_patch *patch)
732
+ {
733
+ if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0) {
734
+ patch->flags = (patch->flags & ~GIT_DIFF_PATCH_DIFFED);
735
+
736
+ patch->hunks_size = 0;
737
+ patch->lines_size = 0;
738
+ }
739
+
740
+ if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0) {
741
+ patch->flags = (patch->flags & ~GIT_DIFF_PATCH_LOADED);
742
+
743
+ release_content(
744
+ &patch->delta->old_file, &patch->old_data, patch->old_blob);
745
+ release_content(
746
+ &patch->delta->new_file, &patch->new_data, patch->new_blob);
747
+ }
748
+ }
749
+
750
+ static void diff_patch_free(git_diff_patch *patch)
751
+ {
752
+ diff_patch_unload(patch);
753
+
754
+ git__free(patch->lines);
755
+ patch->lines = NULL;
756
+ patch->lines_asize = 0;
757
+
758
+ git__free(patch->hunks);
759
+ patch->hunks = NULL;
760
+ patch->hunks_asize = 0;
761
+
762
+ if (!(patch->flags & GIT_DIFF_PATCH_ALLOCATED))
763
+ return;
764
+
765
+ patch->flags = 0;
766
+
767
+ git_diff_list_free(patch->diff); /* decrements refcount */
768
+
769
+ git__free(patch);
770
+ }
771
+
772
+ #define MAX_HUNK_STEP 128
773
+ #define MIN_HUNK_STEP 8
774
+ #define MAX_LINE_STEP 256
775
+ #define MIN_LINE_STEP 8
776
+
777
+ static int diff_patch_hunk_cb(
778
+ void *cb_data,
779
+ const git_diff_delta *delta,
780
+ const git_diff_range *range,
781
+ const char *header,
782
+ size_t header_len)
783
+ {
784
+ git_diff_patch *patch = cb_data;
785
+ diff_patch_hunk *hunk;
786
+
787
+ GIT_UNUSED(delta);
788
+
789
+ if (patch->hunks_size >= patch->hunks_asize) {
790
+ size_t new_size;
791
+ diff_patch_hunk *new_hunks;
792
+
793
+ if (patch->hunks_asize > MAX_HUNK_STEP)
794
+ new_size = patch->hunks_asize + MAX_HUNK_STEP;
795
+ else
796
+ new_size = patch->hunks_asize * 2;
797
+ if (new_size < MIN_HUNK_STEP)
798
+ new_size = MIN_HUNK_STEP;
799
+
800
+ new_hunks = git__realloc(
801
+ patch->hunks, new_size * sizeof(diff_patch_hunk));
802
+ if (!new_hunks)
803
+ return -1;
804
+
805
+ patch->hunks = new_hunks;
806
+ patch->hunks_asize = new_size;
807
+ }
808
+
809
+ hunk = &patch->hunks[patch->hunks_size++];
810
+
811
+ memcpy(&hunk->range, range, sizeof(hunk->range));
812
+
813
+ assert(header_len + 1 < sizeof(hunk->header));
814
+ memcpy(&hunk->header, header, header_len);
815
+ hunk->header[header_len] = '\0';
816
+ hunk->header_len = header_len;
817
+
818
+ hunk->line_start = patch->lines_size;
819
+ hunk->line_count = 0;
820
+
821
+ return 0;
822
+ }
823
+
824
+ static int diff_patch_line_cb(
825
+ void *cb_data,
826
+ const git_diff_delta *delta,
827
+ const git_diff_range *range,
828
+ char line_origin,
829
+ const char *content,
830
+ size_t content_len)
831
+ {
832
+ git_diff_patch *patch = cb_data;
833
+ diff_patch_hunk *hunk;
834
+ diff_patch_line *last, *line;
835
+
836
+ GIT_UNUSED(delta);
837
+ GIT_UNUSED(range);
838
+
839
+ assert(patch->hunks_size > 0);
840
+ assert(patch->hunks != NULL);
841
+
842
+ hunk = &patch->hunks[patch->hunks_size - 1];
843
+
844
+ if (patch->lines_size >= patch->lines_asize) {
845
+ size_t new_size;
846
+ diff_patch_line *new_lines;
847
+
848
+ if (patch->lines_asize > MAX_LINE_STEP)
849
+ new_size = patch->lines_asize + MAX_LINE_STEP;
850
+ else
851
+ new_size = patch->lines_asize * 2;
852
+ if (new_size < MIN_LINE_STEP)
853
+ new_size = MIN_LINE_STEP;
854
+
855
+ new_lines = git__realloc(
856
+ patch->lines, new_size * sizeof(diff_patch_line));
857
+ if (!new_lines)
858
+ return -1;
859
+
860
+ patch->lines = new_lines;
861
+ patch->lines_asize = new_size;
862
+ }
863
+
864
+ last = (patch->lines_size > 0) ?
865
+ &patch->lines[patch->lines_size - 1] : NULL;
866
+ line = &patch->lines[patch->lines_size++];
867
+
868
+ line->ptr = content;
869
+ line->len = content_len;
870
+ line->origin = line_origin;
871
+
872
+ /* do some bookkeeping so we can provide old/new line numbers */
873
+
874
+ for (line->lines = 0; content_len > 0; --content_len) {
875
+ if (*content++ == '\n')
876
+ ++line->lines;
877
+ }
878
+
879
+ if (!last) {
880
+ line->oldno = hunk->range.old_start;
881
+ line->newno = hunk->range.new_start;
882
+ } else {
883
+ switch (last->origin) {
884
+ case GIT_DIFF_LINE_ADDITION:
885
+ line->oldno = last->oldno;
886
+ line->newno = last->newno + last->lines;
887
+ break;
888
+ case GIT_DIFF_LINE_DELETION:
889
+ line->oldno = last->oldno + last->lines;
890
+ line->newno = last->newno;
891
+ break;
892
+ default:
893
+ line->oldno = last->oldno + last->lines;
894
+ line->newno = last->newno + last->lines;
895
+ break;
896
+ }
897
+ }
898
+
899
+ hunk->line_count++;
900
+
901
+ return 0;
902
+ }
903
+
904
+
905
+ int git_diff_foreach(
906
+ git_diff_list *diff,
907
+ void *cb_data,
908
+ git_diff_file_fn file_cb,
909
+ git_diff_hunk_fn hunk_cb,
910
+ git_diff_data_fn data_cb)
911
+ {
912
+ int error = 0;
913
+ diff_context ctxt;
914
+ size_t idx;
915
+ git_diff_patch patch;
916
+
917
+ diff_context_init(
918
+ &ctxt, diff, diff->repo, &diff->opts,
919
+ cb_data, file_cb, hunk_cb, data_cb);
920
+
921
+ diff_patch_init(&ctxt, &patch);
922
+
923
+ git_vector_foreach(&diff->deltas, idx, patch.delta) {
924
+
925
+ /* check flags against patch status */
926
+ if (git_diff_delta__should_skip(ctxt.opts, patch.delta))
927
+ continue;
928
+
929
+ if (!(error = diff_patch_load(&ctxt, &patch))) {
930
+
931
+ /* invoke file callback */
932
+ error = diff_delta_file_callback(&ctxt, patch.delta, idx);
933
+
934
+ /* generate diffs and invoke hunk and line callbacks */
935
+ if (!error)
936
+ error = diff_patch_generate(&ctxt, &patch);
937
+
938
+ diff_patch_unload(&patch);
939
+ }
463
940
 
464
941
  if (error < 0)
465
942
  break;
466
943
  }
467
944
 
945
+ if (error == GIT_EUSER)
946
+ giterr_clear(); /* don't let error message leak */
947
+
468
948
  return error;
469
949
  }
470
950
 
@@ -487,25 +967,33 @@ static char pick_suffix(int mode)
487
967
  return ' ';
488
968
  }
489
969
 
490
- static int print_compact(void *data, git_diff_delta *delta, float progress)
970
+ char git_diff_status_char(git_delta_t status)
971
+ {
972
+ char code;
973
+
974
+ switch (status) {
975
+ case GIT_DELTA_ADDED: code = 'A'; break;
976
+ case GIT_DELTA_DELETED: code = 'D'; break;
977
+ case GIT_DELTA_MODIFIED: code = 'M'; break;
978
+ case GIT_DELTA_RENAMED: code = 'R'; break;
979
+ case GIT_DELTA_COPIED: code = 'C'; break;
980
+ case GIT_DELTA_IGNORED: code = 'I'; break;
981
+ case GIT_DELTA_UNTRACKED: code = '?'; break;
982
+ default: code = ' '; break;
983
+ }
984
+
985
+ return code;
986
+ }
987
+
988
+ static int print_compact(
989
+ void *data, const git_diff_delta *delta, float progress)
491
990
  {
492
991
  diff_print_info *pi = data;
493
- char code, old_suffix, new_suffix;
992
+ char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
494
993
 
495
994
  GIT_UNUSED(progress);
496
995
 
497
- switch (delta->status) {
498
- case GIT_DELTA_ADDED: code = 'A'; break;
499
- case GIT_DELTA_DELETED: code = 'D'; break;
500
- case GIT_DELTA_MODIFIED: code = 'M'; break;
501
- case GIT_DELTA_RENAMED: code = 'R'; break;
502
- case GIT_DELTA_COPIED: code = 'C'; break;
503
- case GIT_DELTA_IGNORED: code = 'I'; break;
504
- case GIT_DELTA_UNTRACKED: code = '?'; break;
505
- default: code = 0;
506
- }
507
-
508
- if (!code)
996
+ if (code == ' ')
509
997
  return 0;
510
998
 
511
999
  old_suffix = pick_suffix(delta->old_file.mode);
@@ -531,7 +1019,10 @@ static int print_compact(void *data, git_diff_delta *delta, float progress)
531
1019
 
532
1020
  if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR,
533
1021
  git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
1022
+ {
1023
+ giterr_clear();
534
1024
  return GIT_EUSER;
1025
+ }
535
1026
 
536
1027
  return 0;
537
1028
  }
@@ -557,8 +1048,7 @@ int git_diff_print_compact(
557
1048
  return error;
558
1049
  }
559
1050
 
560
-
561
- static int print_oid_range(diff_print_info *pi, git_diff_delta *delta)
1051
+ static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
562
1052
  {
563
1053
  char start_oid[8], end_oid[8];
564
1054
 
@@ -588,7 +1078,8 @@ static int print_oid_range(diff_print_info *pi, git_diff_delta *delta)
588
1078
  return 0;
589
1079
  }
590
1080
 
591
- static int print_patch_file(void *data, git_diff_delta *delta, float progress)
1081
+ static int print_patch_file(
1082
+ void *data, const git_diff_delta *delta, float progress)
592
1083
  {
593
1084
  diff_print_info *pi = data;
594
1085
  const char *oldpfx = pi->diff->opts.old_prefix;
@@ -598,6 +1089,9 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
598
1089
 
599
1090
  GIT_UNUSED(progress);
600
1091
 
1092
+ if (S_ISDIR(delta->new_file.mode))
1093
+ return 0;
1094
+
601
1095
  if (!oldpfx)
602
1096
  oldpfx = DIFF_OLD_PREFIX_DEFAULT;
603
1097
 
@@ -627,11 +1121,14 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
627
1121
  if (git_buf_oom(pi->buf))
628
1122
  return -1;
629
1123
 
630
- if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
1124
+ if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
1125
+ {
1126
+ giterr_clear();
631
1127
  return GIT_EUSER;
1128
+ }
632
1129
 
633
- if (delta->binary != 1)
634
- return 0;
1130
+ if (delta->binary != 1)
1131
+ return 0;
635
1132
 
636
1133
  git_buf_clear(pi->buf);
637
1134
  git_buf_printf(
@@ -642,41 +1139,53 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
642
1139
 
643
1140
  if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY,
644
1141
  git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
1142
+ {
1143
+ giterr_clear();
645
1144
  return GIT_EUSER;
1145
+ }
646
1146
 
647
1147
  return 0;
648
1148
  }
649
1149
 
650
1150
  static int print_patch_hunk(
651
1151
  void *data,
652
- git_diff_delta *d,
653
- git_diff_range *r,
1152
+ const git_diff_delta *d,
1153
+ const git_diff_range *r,
654
1154
  const char *header,
655
1155
  size_t header_len)
656
1156
  {
657
1157
  diff_print_info *pi = data;
658
1158
 
1159
+ if (S_ISDIR(d->new_file.mode))
1160
+ return 0;
1161
+
659
1162
  git_buf_clear(pi->buf);
660
1163
  if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
661
1164
  return -1;
662
1165
 
663
1166
  if (pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR,
664
1167
  git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
1168
+ {
1169
+ giterr_clear();
665
1170
  return GIT_EUSER;
1171
+ }
666
1172
 
667
1173
  return 0;
668
1174
  }
669
1175
 
670
1176
  static int print_patch_line(
671
1177
  void *data,
672
- git_diff_delta *delta,
673
- git_diff_range *range,
1178
+ const git_diff_delta *delta,
1179
+ const git_diff_range *range,
674
1180
  char line_origin, /* GIT_DIFF_LINE value from above */
675
1181
  const char *content,
676
1182
  size_t content_len)
677
1183
  {
678
1184
  diff_print_info *pi = data;
679
1185
 
1186
+ if (S_ISDIR(delta->new_file.mode))
1187
+ return 0;
1188
+
680
1189
  git_buf_clear(pi->buf);
681
1190
 
682
1191
  if (line_origin == GIT_DIFF_LINE_ADDITION ||
@@ -691,7 +1200,10 @@ static int print_patch_line(
691
1200
 
692
1201
  if (pi->print_cb(pi->cb_data, delta, range, line_origin,
693
1202
  git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
1203
+ {
1204
+ giterr_clear();
694
1205
  return GIT_EUSER;
1206
+ }
695
1207
 
696
1208
  return 0;
697
1209
  }
@@ -718,94 +1230,275 @@ int git_diff_print_patch(
718
1230
  return error;
719
1231
  }
720
1232
 
1233
+
1234
+ static void set_data_from_blob(
1235
+ git_blob *blob, git_map *map, git_diff_file *file)
1236
+ {
1237
+ if (blob) {
1238
+ map->data = (char *)git_blob_rawcontent(blob);
1239
+ file->size = map->len = git_blob_rawsize(blob);
1240
+ git_oid_cpy(&file->oid, git_object_id((const git_object *)blob));
1241
+ file->mode = 0644;
1242
+ } else {
1243
+ map->data = "";
1244
+ file->size = map->len = 0;
1245
+ file->flags |= GIT_DIFF_FILE_NO_DATA;
1246
+ }
1247
+ }
1248
+
721
1249
  int git_diff_blobs(
722
1250
  git_blob *old_blob,
723
1251
  git_blob *new_blob,
724
- git_diff_options *options,
1252
+ const git_diff_options *options,
725
1253
  void *cb_data,
726
1254
  git_diff_file_fn file_cb,
727
1255
  git_diff_hunk_fn hunk_cb,
728
- git_diff_data_fn line_cb)
1256
+ git_diff_data_fn data_cb)
729
1257
  {
730
- diff_output_info info;
1258
+ int error;
1259
+ git_repository *repo;
1260
+ diff_context ctxt;
731
1261
  git_diff_delta delta;
732
- mmfile_t old_data, new_data;
733
- git_map old_map, new_map;
734
- xpparam_t xdiff_params;
735
- xdemitconf_t xdiff_config;
736
- xdemitcb_t xdiff_callback;
737
- git_blob *new, *old;
738
-
739
- memset(&delta, 0, sizeof(delta));
740
-
741
- new = new_blob;
742
- old = old_blob;
1262
+ git_diff_patch patch;
743
1263
 
744
1264
  if (options && (options->flags & GIT_DIFF_REVERSE)) {
745
- git_blob *swap = old;
746
- old = new;
747
- new = swap;
1265
+ git_blob *swap = old_blob;
1266
+ old_blob = new_blob;
1267
+ new_blob = swap;
748
1268
  }
749
1269
 
750
- if (old) {
751
- old_data.ptr = (char *)git_blob_rawcontent(old);
752
- old_data.size = git_blob_rawsize(old);
753
- git_oid_cpy(&delta.old_file.oid, git_object_id((const git_object *)old));
754
- } else {
755
- old_data.ptr = "";
756
- old_data.size = 0;
757
- }
1270
+ if (new_blob)
1271
+ repo = git_object_owner((git_object *)new_blob);
1272
+ else if (old_blob)
1273
+ repo = git_object_owner((git_object *)old_blob);
1274
+ else
1275
+ repo = NULL;
758
1276
 
759
- if (new) {
760
- new_data.ptr = (char *)git_blob_rawcontent(new);
761
- new_data.size = git_blob_rawsize(new);
762
- git_oid_cpy(&delta.new_file.oid, git_object_id((const git_object *)new));
763
- } else {
764
- new_data.ptr = "";
765
- new_data.size = 0;
766
- }
1277
+ diff_context_init(
1278
+ &ctxt, NULL, repo, options,
1279
+ cb_data, file_cb, hunk_cb, data_cb);
1280
+
1281
+ diff_patch_init(&ctxt, &patch);
1282
+
1283
+ /* create a fake delta record and simulate diff_patch_load */
1284
+
1285
+ memset(&delta, 0, sizeof(delta));
1286
+ delta.binary = -1;
1287
+
1288
+ set_data_from_blob(old_blob, &patch.old_data, &delta.old_file);
1289
+ set_data_from_blob(new_blob, &patch.new_data, &delta.new_file);
767
1290
 
768
- /* populate a "fake" delta record */
769
- delta.status = new ?
770
- (old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
771
- (old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
1291
+ delta.status = new_blob ?
1292
+ (old_blob ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
1293
+ (old_blob ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
772
1294
 
773
1295
  if (git_oid_cmp(&delta.new_file.oid, &delta.old_file.oid) == 0)
774
1296
  delta.status = GIT_DELTA_UNMODIFIED;
775
1297
 
776
- delta.old_file.size = old_data.size;
777
- delta.new_file.size = new_data.size;
1298
+ patch.delta = &delta;
778
1299
 
779
- fill_map_from_mmfile(&old_map, &old_data);
780
- fill_map_from_mmfile(&new_map, &new_data);
1300
+ if ((error = diff_delta_is_binary_by_content(
1301
+ &ctxt, &delta, &delta.old_file, &patch.old_data)) < 0 ||
1302
+ (error = diff_delta_is_binary_by_content(
1303
+ &ctxt, &delta, &delta.new_file, &patch.new_data)) < 0)
1304
+ goto cleanup;
781
1305
 
782
- if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0)
783
- return -1;
1306
+ patch.flags |= GIT_DIFF_PATCH_LOADED;
1307
+ if (delta.binary != 1 && delta.status != GIT_DELTA_UNMODIFIED)
1308
+ patch.flags |= GIT_DIFF_PATCH_DIFFABLE;
784
1309
 
785
- if (file_cb != NULL && file_cb(cb_data, &delta, 1))
786
- return GIT_EUSER;
1310
+ /* do diffs */
787
1311
 
788
- /* don't do hunk and line diffs if the two blobs are identical */
789
- if (delta.status == GIT_DELTA_UNMODIFIED)
1312
+ if (!(error = diff_delta_file_callback(&ctxt, patch.delta, 1)))
1313
+ error = diff_patch_generate(&ctxt, &patch);
1314
+
1315
+ cleanup:
1316
+ diff_patch_unload(&patch);
1317
+
1318
+ if (error == GIT_EUSER)
1319
+ giterr_clear();
1320
+
1321
+ return error;
1322
+ }
1323
+
1324
+
1325
+ size_t git_diff_num_deltas(git_diff_list *diff)
1326
+ {
1327
+ assert(diff);
1328
+ return (size_t)diff->deltas.length;
1329
+ }
1330
+
1331
+ size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
1332
+ {
1333
+ size_t i, count = 0;
1334
+ git_diff_delta *delta;
1335
+
1336
+ assert(diff);
1337
+
1338
+ git_vector_foreach(&diff->deltas, i, delta) {
1339
+ count += (delta->status == type);
1340
+ }
1341
+
1342
+ return count;
1343
+ }
1344
+
1345
+ int git_diff_get_patch(
1346
+ git_diff_patch **patch_ptr,
1347
+ const git_diff_delta **delta_ptr,
1348
+ git_diff_list *diff,
1349
+ size_t idx)
1350
+ {
1351
+ int error;
1352
+ diff_context ctxt;
1353
+ git_diff_delta *delta;
1354
+ git_diff_patch *patch;
1355
+
1356
+ if (patch_ptr)
1357
+ *patch_ptr = NULL;
1358
+
1359
+ delta = git_vector_get(&diff->deltas, idx);
1360
+ if (!delta) {
1361
+ if (delta_ptr)
1362
+ *delta_ptr = NULL;
1363
+ giterr_set(GITERR_INVALID, "Index out of range for delta in diff");
1364
+ return GIT_ENOTFOUND;
1365
+ }
1366
+
1367
+ if (delta_ptr)
1368
+ *delta_ptr = delta;
1369
+
1370
+ if (!patch_ptr &&
1371
+ (delta->binary != -1 ||
1372
+ (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
790
1373
  return 0;
791
1374
 
792
- /* don't do hunk and line diffs if file is binary */
793
- if (delta.binary == 1)
1375
+ diff_context_init(
1376
+ &ctxt, diff, diff->repo, &diff->opts,
1377
+ NULL, NULL, diff_patch_hunk_cb, diff_patch_line_cb);
1378
+
1379
+ if (git_diff_delta__should_skip(ctxt.opts, delta))
794
1380
  return 0;
795
1381
 
796
- memset(&info, 0, sizeof(info));
797
- info.diff = NULL;
798
- info.delta = &delta;
799
- info.cb_data = cb_data;
800
- info.hunk_cb = hunk_cb;
801
- info.line_cb = line_cb;
1382
+ patch = diff_patch_alloc(&ctxt, delta);
1383
+ if (!patch)
1384
+ return -1;
802
1385
 
803
- setup_xdiff_options(options, &xdiff_config, &xdiff_params);
804
- memset(&xdiff_callback, 0, sizeof(xdiff_callback));
805
- xdiff_callback.outf = diff_output_cb;
806
- xdiff_callback.priv = &info;
1386
+ if (!(error = diff_patch_load(&ctxt, patch))) {
1387
+ ctxt.cb_data = patch;
1388
+
1389
+ error = diff_patch_generate(&ctxt, patch);
1390
+
1391
+ if (error == GIT_EUSER)
1392
+ error = ctxt.cb_error;
1393
+ }
1394
+
1395
+ if (error)
1396
+ git_diff_patch_free(patch);
1397
+ else if (patch_ptr)
1398
+ *patch_ptr = patch;
1399
+
1400
+ return error;
1401
+ }
1402
+
1403
+ void git_diff_patch_free(git_diff_patch *patch)
1404
+ {
1405
+ if (patch)
1406
+ GIT_REFCOUNT_DEC(patch, diff_patch_free);
1407
+ }
1408
+
1409
+ const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
1410
+ {
1411
+ assert(patch);
1412
+ return patch->delta;
1413
+ }
1414
+
1415
+ size_t git_diff_patch_num_hunks(git_diff_patch *patch)
1416
+ {
1417
+ assert(patch);
1418
+ return patch->hunks_size;
1419
+ }
1420
+
1421
+ int git_diff_patch_get_hunk(
1422
+ const git_diff_range **range,
1423
+ const char **header,
1424
+ size_t *header_len,
1425
+ size_t *lines_in_hunk,
1426
+ git_diff_patch *patch,
1427
+ size_t hunk_idx)
1428
+ {
1429
+ diff_patch_hunk *hunk;
1430
+
1431
+ assert(patch);
1432
+
1433
+ if (hunk_idx >= patch->hunks_size) {
1434
+ if (range) *range = NULL;
1435
+ if (header) *header = NULL;
1436
+ if (header_len) *header_len = 0;
1437
+ if (lines_in_hunk) *lines_in_hunk = 0;
1438
+ return GIT_ENOTFOUND;
1439
+ }
807
1440
 
808
- xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
1441
+ hunk = &patch->hunks[hunk_idx];
809
1442
 
810
- return info.error;
1443
+ if (range) *range = &hunk->range;
1444
+ if (header) *header = hunk->header;
1445
+ if (header_len) *header_len = hunk->header_len;
1446
+ if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
1447
+
1448
+ return 0;
1449
+ }
1450
+
1451
+ int git_diff_patch_num_lines_in_hunk(
1452
+ git_diff_patch *patch,
1453
+ size_t hunk_idx)
1454
+ {
1455
+ assert(patch);
1456
+
1457
+ if (hunk_idx >= patch->hunks_size)
1458
+ return GIT_ENOTFOUND;
1459
+ else
1460
+ return (int)patch->hunks[hunk_idx].line_count;
811
1461
  }
1462
+
1463
+ int git_diff_patch_get_line_in_hunk(
1464
+ char *line_origin,
1465
+ const char **content,
1466
+ size_t *content_len,
1467
+ int *old_lineno,
1468
+ int *new_lineno,
1469
+ git_diff_patch *patch,
1470
+ size_t hunk_idx,
1471
+ size_t line_of_hunk)
1472
+ {
1473
+ diff_patch_hunk *hunk;
1474
+ diff_patch_line *line;
1475
+
1476
+ assert(patch);
1477
+
1478
+ if (hunk_idx >= patch->hunks_size)
1479
+ goto notfound;
1480
+ hunk = &patch->hunks[hunk_idx];
1481
+
1482
+ if (line_of_hunk >= hunk->line_count)
1483
+ goto notfound;
1484
+
1485
+ line = &patch->lines[hunk->line_start + line_of_hunk];
1486
+
1487
+ if (line_origin) *line_origin = line->origin;
1488
+ if (content) *content = line->ptr;
1489
+ if (content_len) *content_len = line->len;
1490
+ if (old_lineno) *old_lineno = line->oldno;
1491
+ if (new_lineno) *new_lineno = line->newno;
1492
+
1493
+ return 0;
1494
+
1495
+ notfound:
1496
+ if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
1497
+ if (content) *content = NULL;
1498
+ if (content_len) *content_len = 0;
1499
+ if (old_lineno) *old_lineno = -1;
1500
+ if (new_lineno) *new_lineno = -1;
1501
+
1502
+ return GIT_ENOTFOUND;
1503
+ }
1504
+