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
@@ -0,0 +1,112 @@
1
+ /*
2
+ * diff-delta code taken from git.git. See diff-delta.c for details.
3
+ *
4
+ */
5
+ #ifndef INCLUDE_git_delta_h__
6
+ #define INCLUDE_git_delta_h__
7
+
8
+ #include "common.h"
9
+
10
+ /* opaque object for delta index */
11
+ struct git_delta_index;
12
+
13
+ /*
14
+ * create_delta_index: compute index data from given buffer
15
+ *
16
+ * This returns a pointer to a struct delta_index that should be passed to
17
+ * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
18
+ * is returned on failure. The given buffer must not be freed nor altered
19
+ * before free_delta_index() is called. The returned pointer must be freed
20
+ * using free_delta_index().
21
+ */
22
+ extern struct git_delta_index *
23
+ git_delta_create_index(const void *buf, unsigned long bufsize);
24
+
25
+ /*
26
+ * free_delta_index: free the index created by create_delta_index()
27
+ *
28
+ * Given pointer must be what create_delta_index() returned, or NULL.
29
+ */
30
+ extern void git_delta_free_index(struct git_delta_index *index);
31
+
32
+ /*
33
+ * sizeof_delta_index: returns memory usage of delta index
34
+ *
35
+ * Given pointer must be what create_delta_index() returned, or NULL.
36
+ */
37
+ extern unsigned long git_delta_sizeof_index(struct git_delta_index *index);
38
+
39
+ /*
40
+ * create_delta: create a delta from given index for the given buffer
41
+ *
42
+ * This function may be called multiple times with different buffers using
43
+ * the same delta_index pointer. If max_delta_size is non-zero and the
44
+ * resulting delta is to be larger than max_delta_size then NULL is returned.
45
+ * On success, a non-NULL pointer to the buffer with the delta data is
46
+ * returned and *delta_size is updated with its size. The returned buffer
47
+ * must be freed by the caller.
48
+ */
49
+ extern void *
50
+ git_delta_create(const struct git_delta_index *index,
51
+ const void *buf, unsigned long bufsize,
52
+ unsigned long *delta_size,
53
+ unsigned long max_delta_size);
54
+
55
+ /*
56
+ * diff_delta: create a delta from source buffer to target buffer
57
+ *
58
+ * If max_delta_size is non-zero and the resulting delta is to be larger
59
+ * than max_delta_size then NULL is returned. On success, a non-NULL
60
+ * pointer to the buffer with the delta data is returned and *delta_size is
61
+ * updated with its size. The returned buffer must be freed by the caller.
62
+ */
63
+ GIT_INLINE(void *)
64
+ git_delta(const void *src_buf, unsigned long src_bufsize,
65
+ const void *trg_buf, unsigned long trg_bufsize,
66
+ unsigned long *delta_size, unsigned long max_delta_size)
67
+ {
68
+ struct git_delta_index *index = git_delta_create_index(src_buf, src_bufsize);
69
+ if (index) {
70
+ void *delta = git_delta_create(index, trg_buf, trg_bufsize,
71
+ delta_size, max_delta_size);
72
+ git_delta_free_index(index);
73
+ return delta;
74
+ }
75
+ return NULL;
76
+ }
77
+
78
+ /*
79
+ * patch_delta: recreate target buffer given source buffer and delta data
80
+ *
81
+ * On success, a non-NULL pointer to the target buffer is returned and
82
+ * *trg_bufsize is updated with its size. On failure a NULL pointer is
83
+ * returned. The returned buffer must be freed by the caller.
84
+ */
85
+ extern void *git_delta_patch(const void *src_buf, unsigned long src_size,
86
+ const void *delta_buf, unsigned long delta_size,
87
+ unsigned long *dst_size);
88
+
89
+ /* the smallest possible delta size is 4 bytes */
90
+ #define GIT_DELTA_SIZE_MIN 4
91
+
92
+ /*
93
+ * This must be called twice on the delta data buffer, first to get the
94
+ * expected source buffer size, and again to get the target buffer size.
95
+ */
96
+ GIT_INLINE(unsigned long)
97
+ git_delta_get_hdr_size(const unsigned char **datap,
98
+ const unsigned char *top)
99
+ {
100
+ const unsigned char *data = *datap;
101
+ unsigned long cmd, size = 0;
102
+ int i = 0;
103
+ do {
104
+ cmd = *data++;
105
+ size |= (cmd & 0x7f) << i;
106
+ i += 7;
107
+ } while (cmd & 0x80 && data < top);
108
+ *datap = data;
109
+ return size;
110
+ }
111
+
112
+ #endif
@@ -5,12 +5,11 @@
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
- #include "git2/oid.h"
10
8
  #include "diff.h"
11
9
  #include "fileops.h"
12
10
  #include "config.h"
13
11
  #include "attr_file.h"
12
+ #include "filter.h"
14
13
 
15
14
  static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
16
15
  {
@@ -63,8 +62,8 @@ static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
63
62
 
64
63
  git_vector_foreach(&diff->pathspec, i, match) {
65
64
  int result = strcmp(match->pattern, path) ? FNM_NOMATCH : 0;
66
-
67
- if (((diff->opts.flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH) == 0) &&
65
+
66
+ if (((diff->opts.flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH) == 0) &&
68
67
  result == FNM_NOMATCH)
69
68
  result = p_fnmatch(match->pattern, path, 0);
70
69
 
@@ -225,7 +224,10 @@ static int diff_delta__from_one(
225
224
  }
226
225
 
227
226
  delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
228
- delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
227
+
228
+ if (delta->status == GIT_DELTA_DELETED ||
229
+ !git_oid_iszero(&delta->new_file.oid))
230
+ delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
229
231
 
230
232
  if (git_vector_insert(&diff->deltas, delta) < 0) {
231
233
  git__free(delta);
@@ -262,12 +264,22 @@ static int diff_delta__from_two(
262
264
  delta = diff_delta__alloc(diff, status, old_entry->path);
263
265
  GITERR_CHECK_ALLOC(delta);
264
266
 
265
- delta->old_file.mode = old_mode;
266
267
  git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
268
+ delta->old_file.size = old_entry->file_size;
269
+ delta->old_file.mode = old_mode;
267
270
  delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
268
271
 
272
+ git_oid_cpy(&delta->new_file.oid, &new_entry->oid);
273
+ delta->new_file.size = new_entry->file_size;
269
274
  delta->new_file.mode = new_mode;
270
- git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
275
+
276
+ if (new_oid) {
277
+ if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0)
278
+ git_oid_cpy(&delta->old_file.oid, new_oid);
279
+ else
280
+ git_oid_cpy(&delta->new_file.oid, new_oid);
281
+ }
282
+
271
283
  if (new_oid || !git_oid_iszero(&new_entry->oid))
272
284
  delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
273
285
 
@@ -279,6 +291,36 @@ static int diff_delta__from_two(
279
291
  return 0;
280
292
  }
281
293
 
294
+ static git_diff_delta *diff_delta__last_for_item(
295
+ git_diff_list *diff,
296
+ const git_index_entry *item)
297
+ {
298
+ git_diff_delta *delta = git_vector_last(&diff->deltas);
299
+ if (!delta)
300
+ return NULL;
301
+
302
+ switch (delta->status) {
303
+ case GIT_DELTA_UNMODIFIED:
304
+ case GIT_DELTA_DELETED:
305
+ if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0)
306
+ return delta;
307
+ break;
308
+ case GIT_DELTA_ADDED:
309
+ if (git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
310
+ return delta;
311
+ break;
312
+ case GIT_DELTA_MODIFIED:
313
+ if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0 ||
314
+ git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
315
+ return delta;
316
+ break;
317
+ default:
318
+ break;
319
+ }
320
+
321
+ return NULL;
322
+ }
323
+
282
324
  static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
283
325
  {
284
326
  size_t len = strlen(prefix);
@@ -316,6 +358,7 @@ static git_diff_list *git_diff_list_alloc(
316
358
  if (diff == NULL)
317
359
  return NULL;
318
360
 
361
+ GIT_REFCOUNT_INC(diff);
319
362
  diff->repo = repo;
320
363
 
321
364
  if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
@@ -355,6 +398,10 @@ static git_diff_list *git_diff_list_alloc(
355
398
  diff->opts.new_prefix = swap;
356
399
  }
357
400
 
401
+ /* INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
402
+ if (diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES)
403
+ diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
404
+
358
405
  /* only copy pathspec if it is "interesting" so we can test
359
406
  * diff->pathspec.length > 0 to know if it is worth calling
360
407
  * fnmatch as we iterate.
@@ -391,15 +438,12 @@ fail:
391
438
  return NULL;
392
439
  }
393
440
 
394
- void git_diff_list_free(git_diff_list *diff)
441
+ static void diff_list_free(git_diff_list *diff)
395
442
  {
396
443
  git_diff_delta *delta;
397
444
  git_attr_fnmatch *match;
398
445
  unsigned int i;
399
446
 
400
- if (!diff)
401
- return;
402
-
403
447
  git_vector_foreach(&diff->deltas, i, delta) {
404
448
  git__free(delta);
405
449
  diff->deltas.contents[i] = NULL;
@@ -416,32 +460,68 @@ void git_diff_list_free(git_diff_list *diff)
416
460
  git__free(diff);
417
461
  }
418
462
 
463
+ void git_diff_list_free(git_diff_list *diff)
464
+ {
465
+ if (!diff)
466
+ return;
467
+
468
+ GIT_REFCOUNT_DEC(diff, diff_list_free);
469
+ }
470
+
471
+ void git_diff_list_addref(git_diff_list *diff)
472
+ {
473
+ GIT_REFCOUNT_INC(diff);
474
+ }
475
+
419
476
  static int oid_for_workdir_item(
420
477
  git_repository *repo,
421
478
  const git_index_entry *item,
422
479
  git_oid *oid)
423
480
  {
424
- int result;
481
+ int result = 0;
425
482
  git_buf full_path = GIT_BUF_INIT;
426
483
 
427
- if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)
484
+ if (git_buf_joinpath(
485
+ &full_path, git_repository_workdir(repo), item->path) < 0)
428
486
  return -1;
429
487
 
430
- /* calculate OID for file if possible*/
431
- if (S_ISLNK(item->mode))
488
+ /* calculate OID for file if possible */
489
+ if (S_ISGITLINK(item->mode)) {
490
+ git_submodule *sm;
491
+ const git_oid *sm_oid;
492
+
493
+ if (!git_submodule_lookup(&sm, repo, item->path) &&
494
+ (sm_oid = git_submodule_wd_oid(sm)) != NULL)
495
+ git_oid_cpy(oid, sm_oid);
496
+ else {
497
+ /* if submodule lookup failed probably just in an intermediate
498
+ * state where some init hasn't happened, so ignore the error
499
+ */
500
+ giterr_clear();
501
+ memset(oid, 0, sizeof(*oid));
502
+ }
503
+ } else if (S_ISLNK(item->mode))
432
504
  result = git_odb__hashlink(oid, full_path.ptr);
433
505
  else if (!git__is_sizet(item->file_size)) {
434
506
  giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
435
507
  result = -1;
436
508
  } else {
437
- int fd = git_futils_open_ro(full_path.ptr);
438
- if (fd < 0)
439
- result = fd;
440
- else {
441
- result = git_odb__hashfd(
442
- oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);
443
- p_close(fd);
509
+ git_vector filters = GIT_VECTOR_INIT;
510
+
511
+ result = git_filters_load(
512
+ &filters, repo, item->path, GIT_FILTER_TO_ODB);
513
+ if (result >= 0) {
514
+ int fd = git_futils_open_ro(full_path.ptr);
515
+ if (fd < 0)
516
+ result = fd;
517
+ else {
518
+ result = git_odb__hashfd_filtered(
519
+ oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB, &filters);
520
+ p_close(fd);
521
+ }
444
522
  }
523
+
524
+ git_filters_free(&filters);
445
525
  }
446
526
 
447
527
  git_buf_free(&full_path);
@@ -462,6 +542,7 @@ static int maybe_modified(
462
542
  git_delta_t status = GIT_DELTA_MODIFIED;
463
543
  unsigned int omode = oitem->mode;
464
544
  unsigned int nmode = nitem->mode;
545
+ bool new_is_workdir = (new_iter->type == GIT_ITERATOR_WORKDIR);
465
546
 
466
547
  GIT_UNUSED(old_iter);
467
548
 
@@ -469,15 +550,14 @@ static int maybe_modified(
469
550
  return 0;
470
551
 
471
552
  /* on platforms with no symlinks, preserve mode of existing symlinks */
472
- if (S_ISLNK(omode) && S_ISREG(nmode) &&
473
- !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS) &&
474
- new_iter->type == GIT_ITERATOR_WORKDIR)
553
+ if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir &&
554
+ !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
475
555
  nmode = omode;
476
556
 
477
557
  /* on platforms with no execmode, just preserve old mode */
478
558
  if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
479
559
  (nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) &&
480
- new_iter->type == GIT_ITERATOR_WORKDIR)
560
+ new_is_workdir)
481
561
  nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
482
562
 
483
563
  /* support "assume unchanged" (poorly, b/c we still stat everything) */
@@ -491,10 +571,14 @@ static int maybe_modified(
491
571
 
492
572
  /* if basic type of file changed, then split into delete and add */
493
573
  else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
494
- if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
495
- diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
496
- return -1;
497
- return 0;
574
+ if ((diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE) != 0)
575
+ status = GIT_DELTA_TYPECHANGE;
576
+ else {
577
+ if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
578
+ diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
579
+ return -1;
580
+ return 0;
581
+ }
498
582
  }
499
583
 
500
584
  /* if oids and modes match, then file is unmodified */
@@ -502,17 +586,15 @@ static int maybe_modified(
502
586
  omode == nmode)
503
587
  status = GIT_DELTA_UNMODIFIED;
504
588
 
505
- /* if modes match and we have an unknown OID and a workdir iterator,
506
- * then check deeper for matching
589
+ /* if we have an unknown OID and a workdir iterator, then check some
590
+ * circumstances that can accelerate things or need special handling
507
591
  */
508
- else if (omode == nmode &&
509
- git_oid_iszero(&nitem->oid) &&
510
- new_iter->type == GIT_ITERATOR_WORKDIR)
511
- {
592
+ else if (git_oid_iszero(&nitem->oid) && new_is_workdir) {
512
593
  /* TODO: add check against index file st_mtime to avoid racy-git */
513
594
 
514
- /* if they files look exactly alike, then we'll assume the same */
515
- if (oitem->file_size == nitem->file_size &&
595
+ /* if the stat data looks exactly alike, then assume the same */
596
+ if (omode == nmode &&
597
+ oitem->file_size == nitem->file_size &&
516
598
  (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
517
599
  (oitem->ctime.seconds == nitem->ctime.seconds)) &&
518
600
  oitem->mtime.seconds == nitem->mtime.seconds &&
@@ -530,23 +612,34 @@ static int maybe_modified(
530
612
  status = GIT_DELTA_UNMODIFIED;
531
613
  else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
532
614
  return -1;
533
- else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
615
+ else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
534
616
  status = GIT_DELTA_UNMODIFIED;
535
617
  else {
536
- /* TODO: support other GIT_SUBMODULE_IGNORE values */
537
- status = GIT_DELTA_UNMODIFIED;
618
+ unsigned int sm_status = 0;
619
+ if (git_submodule_status(&sm_status, sub) < 0)
620
+ return -1;
621
+ status = GIT_SUBMODULE_STATUS_IS_UNMODIFIED(sm_status)
622
+ ? GIT_DELTA_UNMODIFIED : GIT_DELTA_MODIFIED;
623
+
624
+ /* grab OID while we are here */
625
+ if (git_oid_iszero(&nitem->oid)) {
626
+ const git_oid *sm_oid = git_submodule_wd_oid(sub);
627
+ if (sm_oid != NULL) {
628
+ git_oid_cpy(&noid, sm_oid);
629
+ use_noid = &noid;
630
+ }
631
+ }
538
632
  }
539
633
  }
634
+ }
540
635
 
541
- /* TODO: check git attributes so we will not have to read the file
542
- * in if it is marked binary.
543
- */
544
-
545
- else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
636
+ /* if we got here and decided that the files are modified, but we
637
+ * haven't calculated the OID of the new item, then calculate it now
638
+ */
639
+ if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
640
+ if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
546
641
  return -1;
547
-
548
- else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
549
- omode == nmode)
642
+ else if (omode == nmode && git_oid_equal(&oitem->oid, &noid))
550
643
  status = GIT_DELTA_UNMODIFIED;
551
644
 
552
645
  /* store calculated oid so we don't have to recalc later */
@@ -557,6 +650,40 @@ static int maybe_modified(
557
650
  diff, status, oitem, omode, nitem, nmode, use_noid);
558
651
  }
559
652
 
653
+ static int git_index_entry_cmp_case(const void *a, const void *b)
654
+ {
655
+ const git_index_entry *entry_a = a;
656
+ const git_index_entry *entry_b = b;
657
+
658
+ return strcmp(entry_a->path, entry_b->path);
659
+ }
660
+
661
+ static int git_index_entry_cmp_icase(const void *a, const void *b)
662
+ {
663
+ const git_index_entry *entry_a = a;
664
+ const git_index_entry *entry_b = b;
665
+
666
+ return strcasecmp(entry_a->path, entry_b->path);
667
+ }
668
+
669
+ static bool entry_is_prefixed(
670
+ const git_index_entry *item,
671
+ git_iterator *prefix_iterator,
672
+ const git_index_entry *prefix_item)
673
+ {
674
+ size_t pathlen;
675
+
676
+ if (!prefix_item ||
677
+ ITERATOR_PREFIXCMP(*prefix_iterator, prefix_item->path, item->path))
678
+ return false;
679
+
680
+ pathlen = strlen(item->path);
681
+
682
+ return (item->path[pathlen - 1] == '/' ||
683
+ prefix_item->path[pathlen] == '\0' ||
684
+ prefix_item->path[pathlen] == '/');
685
+ }
686
+
560
687
  static int diff_from_iterators(
561
688
  git_repository *repo,
562
689
  const git_diff_options *opts, /**< can be NULL for defaults */
@@ -567,12 +694,36 @@ static int diff_from_iterators(
567
694
  const git_index_entry *oitem, *nitem;
568
695
  git_buf ignore_prefix = GIT_BUF_INIT;
569
696
  git_diff_list *diff = git_diff_list_alloc(repo, opts);
697
+ git_vector_cmp entry_compare;
698
+
570
699
  if (!diff)
571
700
  goto fail;
572
701
 
573
702
  diff->old_src = old_iter->type;
574
703
  diff->new_src = new_iter->type;
575
704
 
705
+ /* Use case-insensitive compare if either iterator has
706
+ * the ignore_case bit set */
707
+ if (!old_iter->ignore_case && !new_iter->ignore_case) {
708
+ entry_compare = git_index_entry_cmp_case;
709
+ diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
710
+ } else {
711
+ entry_compare = git_index_entry_cmp_icase;
712
+ diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
713
+
714
+ /* If one of the iterators doesn't have ignore_case set,
715
+ * then that's unfortunate because we'll have to spool
716
+ * its data, sort it icase, and then use that for our
717
+ * merge join to the other iterator that is icase sorted */
718
+ if (!old_iter->ignore_case) {
719
+ if (git_iterator_spoolandsort(&old_iter, old_iter, git_index_entry_cmp_icase, true) < 0)
720
+ goto fail;
721
+ } else if (!new_iter->ignore_case) {
722
+ if (git_iterator_spoolandsort(&new_iter, new_iter, git_index_entry_cmp_icase, true) < 0)
723
+ goto fail;
724
+ }
725
+ }
726
+
576
727
  if (git_iterator_current(old_iter, &oitem) < 0 ||
577
728
  git_iterator_current(new_iter, &nitem) < 0)
578
729
  goto fail;
@@ -581,21 +732,38 @@ static int diff_from_iterators(
581
732
  while (oitem || nitem) {
582
733
 
583
734
  /* create DELETED records for old items not matched in new */
584
- if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {
585
- if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
586
- git_iterator_advance(old_iter, &oitem) < 0)
735
+ if (oitem && (!nitem || entry_compare(oitem, nitem) < 0)) {
736
+ if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0)
737
+ goto fail;
738
+
739
+ /* if we are generating TYPECHANGE records then check for that
740
+ * instead of just generating a DELETE record
741
+ */
742
+ if ((diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 &&
743
+ entry_is_prefixed(oitem, new_iter, nitem))
744
+ {
745
+ /* this entry has become a tree! convert to TYPECHANGE */
746
+ git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
747
+ if (last) {
748
+ last->status = GIT_DELTA_TYPECHANGE;
749
+ last->new_file.mode = GIT_FILEMODE_TREE;
750
+ }
751
+ }
752
+
753
+ if (git_iterator_advance(old_iter, &oitem) < 0)
587
754
  goto fail;
588
755
  }
589
756
 
590
757
  /* create ADDED, TRACKED, or IGNORED records for new items not
591
758
  * matched in old (and/or descend into directories as needed)
592
759
  */
593
- else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
760
+ else if (nitem && (!oitem || entry_compare(oitem, nitem) > 0)) {
594
761
  git_delta_t delta_type = GIT_DELTA_UNTRACKED;
595
762
 
596
763
  /* check if contained in ignored parent directory */
597
764
  if (git_buf_len(&ignore_prefix) &&
598
- git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
765
+ ITERATOR_PREFIXCMP(*old_iter, nitem->path,
766
+ git_buf_cstr(&ignore_prefix)) == 0)
599
767
  delta_type = GIT_DELTA_IGNORED;
600
768
 
601
769
  if (S_ISDIR(nitem->mode)) {
@@ -603,10 +771,22 @@ static int diff_from_iterators(
603
771
  * it or if the user requested the contents of untracked
604
772
  * directories and it is not under an ignored directory.
605
773
  */
606
- if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) ||
774
+ bool contains_tracked =
775
+ entry_is_prefixed(nitem, old_iter, oitem);
776
+ bool recurse_untracked =
607
777
  (delta_type == GIT_DELTA_UNTRACKED &&
608
- (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))
609
- {
778
+ (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0);
779
+
780
+ /* do not advance into directories that contain a .git file */
781
+ if (!contains_tracked && recurse_untracked) {
782
+ git_buf *full = NULL;
783
+ if (git_iterator_current_workdir_path(new_iter, &full) < 0)
784
+ goto fail;
785
+ if (git_path_contains_dir(full, DOT_GIT))
786
+ recurse_untracked = false;
787
+ }
788
+
789
+ if (contains_tracked || recurse_untracked) {
610
790
  /* if this directory is ignored, remember it as the
611
791
  * "ignore_prefix" for processing contained items
612
792
  */
@@ -648,8 +828,25 @@ static int diff_from_iterators(
648
828
  else if (new_iter->type != GIT_ITERATOR_WORKDIR)
649
829
  delta_type = GIT_DELTA_ADDED;
650
830
 
651
- if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||
652
- git_iterator_advance(new_iter, &nitem) < 0)
831
+ if (diff_delta__from_one(diff, delta_type, nitem) < 0)
832
+ goto fail;
833
+
834
+ /* if we are generating TYPECHANGE records then check for that
835
+ * instead of just generating an ADD/UNTRACKED record
836
+ */
837
+ if (delta_type != GIT_DELTA_IGNORED &&
838
+ (diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 &&
839
+ entry_is_prefixed(nitem, old_iter, oitem))
840
+ {
841
+ /* this entry was a tree! convert to TYPECHANGE */
842
+ git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
843
+ if (last) {
844
+ last->status = GIT_DELTA_TYPECHANGE;
845
+ last->old_file.mode = GIT_FILEMODE_TREE;
846
+ }
847
+ }
848
+
849
+ if (git_iterator_advance(new_iter, &nitem) < 0)
653
850
  goto fail;
654
851
  }
655
852
 
@@ -657,7 +854,7 @@ static int diff_from_iterators(
657
854
  * (or ADDED and DELETED pair if type changed)
658
855
  */
659
856
  else {
660
- assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
857
+ assert(oitem && nitem && entry_compare(oitem, nitem) == 0);
661
858
 
662
859
  if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
663
860
  git_iterator_advance(old_iter, &oitem) < 0 ||
@@ -736,12 +933,14 @@ int git_diff_workdir_to_index(
736
933
  git_diff_list **diff)
737
934
  {
738
935
  git_iterator *a = NULL, *b = NULL;
936
+ int error;
937
+
739
938
  char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
740
939
 
741
940
  assert(repo && diff);
742
941
 
743
- if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||
744
- git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
942
+ if ((error = git_iterator_for_index_range(&a, repo, prefix, prefix)) < 0 ||
943
+ (error = git_iterator_for_workdir_range(&b, repo, prefix, prefix)) < 0)
745
944
  goto on_error;
746
945
 
747
946
  git__free(prefix);
@@ -751,7 +950,7 @@ int git_diff_workdir_to_index(
751
950
  on_error:
752
951
  git__free(prefix);
753
952
  git_iterator_free(a);
754
- return -1;
953
+ return error;
755
954
  }
756
955
 
757
956
 
@@ -762,12 +961,14 @@ int git_diff_workdir_to_tree(
762
961
  git_diff_list **diff)
763
962
  {
764
963
  git_iterator *a = NULL, *b = NULL;
964
+ int error;
965
+
765
966
  char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
766
967
 
767
968
  assert(repo && old_tree && diff);
768
969
 
769
- if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
770
- git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
970
+ if ((error = git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix)) < 0 ||
971
+ (error = git_iterator_for_workdir_range(&b, repo, prefix, prefix)) < 0)
771
972
  goto on_error;
772
973
 
773
974
  git__free(prefix);
@@ -777,9 +978,31 @@ int git_diff_workdir_to_tree(
777
978
  on_error:
778
979
  git__free(prefix);
779
980
  git_iterator_free(a);
780
- return -1;
981
+ return error;
781
982
  }
782
983
 
984
+
985
+ bool git_diff_delta__should_skip(
986
+ const git_diff_options *opts, const git_diff_delta *delta)
987
+ {
988
+ uint32_t flags = opts ? opts->flags : 0;
989
+
990
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
991
+ (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
992
+ return true;
993
+
994
+ if (delta->status == GIT_DELTA_IGNORED &&
995
+ (flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
996
+ return true;
997
+
998
+ if (delta->status == GIT_DELTA_UNTRACKED &&
999
+ (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
1000
+ return true;
1001
+
1002
+ return false;
1003
+ }
1004
+
1005
+
783
1006
  int git_diff_merge(
784
1007
  git_diff_list *onto,
785
1008
  const git_diff_list *from)
@@ -788,6 +1011,7 @@ int git_diff_merge(
788
1011
  git_pool onto_pool;
789
1012
  git_vector onto_new;
790
1013
  git_diff_delta *delta;
1014
+ bool ignore_case = false;
791
1015
  unsigned int i, j;
792
1016
 
793
1017
  assert(onto && from);
@@ -799,10 +1023,21 @@ int git_diff_merge(
799
1023
  git_pool_init(&onto_pool, 1, 0) < 0)
800
1024
  return -1;
801
1025
 
1026
+ if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ||
1027
+ (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
1028
+ {
1029
+ ignore_case = true;
1030
+
1031
+ /* This function currently only supports merging diff lists that
1032
+ * are sorted identically. */
1033
+ assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
1034
+ (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0);
1035
+ }
1036
+
802
1037
  for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
803
1038
  git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
804
1039
  const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
805
- int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path);
1040
+ int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
806
1041
 
807
1042
  if (cmp < 0) {
808
1043
  delta = diff_delta__dup(o, &onto_pool);
@@ -816,6 +1051,14 @@ int git_diff_merge(
816
1051
  j++;
817
1052
  }
818
1053
 
1054
+ /* the ignore rules for the target may not match the source
1055
+ * or the result of a merged delta could be skippable...
1056
+ */
1057
+ if (git_diff_delta__should_skip(&onto->opts, delta)) {
1058
+ git__free(delta);
1059
+ continue;
1060
+ }
1061
+
819
1062
  if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
820
1063
  break;
821
1064
  }