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
@@ -125,24 +125,26 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema
125
125
  {
126
126
  time_t now;
127
127
  time_t offset;
128
- struct tm *utc_tm, *local_tm;
128
+ struct tm *utc_tm;
129
129
  git_signature *sig;
130
- struct tm _utc, _local;
130
+ struct tm _utc;
131
131
 
132
132
  *sig_out = NULL;
133
133
 
134
+ /*
135
+ * Get the current time as seconds since the epoch and
136
+ * transform that into a tm struct containing the time at
137
+ * UTC. Give that to mktime which considers it a local time
138
+ * (tm_isdst = -1 asks it to take DST into account) and gives
139
+ * us that time as seconds since the epoch. The difference
140
+ * between its return value and 'now' is our offset to UTC.
141
+ */
134
142
  time(&now);
135
-
136
143
  utc_tm = p_gmtime_r(&now, &_utc);
137
- local_tm = p_localtime_r(&now, &_local);
138
-
139
- offset = mktime(local_tm) - mktime(utc_tm);
144
+ utc_tm->tm_isdst = -1;
145
+ offset = (time_t)difftime(now, mktime(utc_tm));
140
146
  offset /= 60;
141
147
 
142
- /* mktime takes care of setting tm_isdst correctly */
143
- if (local_tm->tm_isdst)
144
- offset += 60;
145
-
146
148
  if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
147
149
  return -1;
148
150
 
@@ -25,7 +25,6 @@ static unsigned int index_delta2status(git_delta_t index_status)
25
25
  switch (index_status) {
26
26
  case GIT_DELTA_ADDED:
27
27
  case GIT_DELTA_COPIED:
28
- case GIT_DELTA_RENAMED:
29
28
  st = GIT_STATUS_INDEX_NEW;
30
29
  break;
31
30
  case GIT_DELTA_DELETED:
@@ -34,6 +33,12 @@ static unsigned int index_delta2status(git_delta_t index_status)
34
33
  case GIT_DELTA_MODIFIED:
35
34
  st = GIT_STATUS_INDEX_MODIFIED;
36
35
  break;
36
+ case GIT_DELTA_RENAMED:
37
+ st = GIT_STATUS_INDEX_RENAMED;
38
+ break;
39
+ case GIT_DELTA_TYPECHANGE:
40
+ st = GIT_STATUS_INDEX_TYPECHANGE;
41
+ break;
37
42
  default:
38
43
  break;
39
44
  }
@@ -47,8 +52,8 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
47
52
 
48
53
  switch (workdir_status) {
49
54
  case GIT_DELTA_ADDED:
50
- case GIT_DELTA_COPIED:
51
55
  case GIT_DELTA_RENAMED:
56
+ case GIT_DELTA_COPIED:
52
57
  case GIT_DELTA_UNTRACKED:
53
58
  st = GIT_STATUS_WT_NEW;
54
59
  break;
@@ -61,6 +66,9 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
61
66
  case GIT_DELTA_IGNORED:
62
67
  st = GIT_STATUS_IGNORED;
63
68
  break;
69
+ case GIT_DELTA_TYPECHANGE:
70
+ st = GIT_STATUS_WT_TYPECHANGE;
71
+ break;
64
72
  default:
65
73
  break;
66
74
  }
@@ -82,15 +90,22 @@ int git_status_foreach_ext(
82
90
  opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
83
91
  git_diff_delta *i2h, *w2i;
84
92
  size_t i, j, i_max, j_max;
93
+ bool ignore_case = false;
85
94
 
86
95
  assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
87
96
 
97
+ if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
98
+ (err = git_repository__ensure_not_bare(repo, "status")) < 0)
99
+ return err;
100
+
88
101
  if ((err = git_repository_head_tree(&head, repo)) < 0)
89
102
  return err;
90
103
 
91
104
  memset(&diffopt, 0, sizeof(diffopt));
92
105
  memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
93
106
 
107
+ diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
108
+
94
109
  if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
95
110
  diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
96
111
  if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
@@ -124,11 +139,26 @@ int git_status_foreach_ext(
124
139
  i_max = idx2head ? idx2head->deltas.length : 0;
125
140
  j_max = wd2idx ? wd2idx->deltas.length : 0;
126
141
 
142
+ if (idx2head && wd2idx &&
143
+ (0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) ||
144
+ 0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)))
145
+ {
146
+ /* Then use the ignore-case sorter... */
147
+ ignore_case = true;
148
+
149
+ /* and assert that both are ignore-case sorted. If this function
150
+ * ever needs to support merge joining result sets that are not sorted
151
+ * by the same function, then it will need to be extended to do a spool
152
+ * and sort on one of the results before merge joining */
153
+ assert(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) &&
154
+ 0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE));
155
+ }
156
+
127
157
  for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
128
158
  i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
129
159
  w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
130
160
 
131
- cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path);
161
+ cmp = !w2i ? -1 : !i2h ? 1 : STRCMP_CASESELECT(ignore_case, i2h->old_file.path, w2i->old_file.path);
132
162
 
133
163
  if (cmp < 0) {
134
164
  if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
@@ -151,6 +181,9 @@ cleanup:
151
181
  git_diff_list_free(idx2head);
152
182
  git_diff_list_free(wd2idx);
153
183
 
184
+ if (err == GIT_EUSER)
185
+ giterr_clear();
186
+
154
187
  return err;
155
188
  }
156
189
 
@@ -226,9 +259,22 @@ int git_status_file(
226
259
  error = GIT_EAMBIGUOUS;
227
260
 
228
261
  if (!error && !sfi.count) {
229
- giterr_set(GITERR_INVALID,
230
- "Attempt to get status of nonexistent file '%s'", path);
231
- error = GIT_ENOTFOUND;
262
+ git_buf full = GIT_BUF_INIT;
263
+
264
+ /* if the file actually exists and we still did not get a callback
265
+ * for it, then it must be contained inside an ignored directory, so
266
+ * mark it as such instead of generating an error.
267
+ */
268
+ if (!git_buf_joinpath(&full, git_repository_workdir(repo), path) &&
269
+ git_path_exists(full.ptr))
270
+ sfi.status = GIT_STATUS_IGNORED;
271
+ else {
272
+ giterr_set(GITERR_INVALID,
273
+ "Attempt to get status of nonexistent file '%s'", path);
274
+ error = GIT_ENOTFOUND;
275
+ }
276
+
277
+ git_buf_free(&full);
232
278
  }
233
279
 
234
280
  *status_flags = sfi.status;
@@ -17,18 +17,24 @@
17
17
  #include "config_file.h"
18
18
  #include "config.h"
19
19
  #include "repository.h"
20
+ #include "submodule.h"
21
+ #include "tree.h"
22
+ #include "iterator.h"
23
+
24
+ #define GIT_MODULES_FILE ".gitmodules"
20
25
 
21
26
  static git_cvar_map _sm_update_map[] = {
22
27
  {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
23
28
  {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
24
- {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE}
29
+ {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
30
+ {GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
25
31
  };
26
32
 
27
33
  static git_cvar_map _sm_ignore_map[] = {
28
- {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
29
- {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
34
+ {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
30
35
  {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
31
- {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}
36
+ {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
37
+ {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
32
38
  };
33
39
 
34
40
  static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
@@ -36,7 +42,7 @@ static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
36
42
  khint_t h;
37
43
 
38
44
  for (h = 0; *s; ++s)
39
- if (s[1] || *s != '/')
45
+ if (s[1] != '\0' || *s != '/')
40
46
  h = (h << 5) - h + *s;
41
47
 
42
48
  return h;
@@ -47,341 +53,1443 @@ static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
47
53
  size_t alen = a ? strlen(a) : 0;
48
54
  size_t blen = b ? strlen(b) : 0;
49
55
 
50
- if (alen && a[alen] == '/')
56
+ if (alen > 0 && a[alen - 1] == '/')
51
57
  alen--;
52
- if (blen && b[blen] == '/')
58
+ if (blen > 0 && b[blen - 1] == '/')
53
59
  blen--;
54
60
 
55
61
  return (alen == blen && strncmp(a, b, alen) == 0);
56
62
  }
57
63
 
58
- __KHASH_IMPL(str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash);
64
+ __KHASH_IMPL(
65
+ str, static kh_inline, const char *, void *, 1,
66
+ str_hash_no_trailing_slash, str_equal_no_trailing_slash);
67
+
68
+ static int load_submodule_config(git_repository *repo, bool force);
69
+ static git_config_file *open_gitmodules(git_repository *, bool, const git_oid *);
70
+ static int lookup_head_remote(git_buf *url, git_repository *repo);
71
+ static int submodule_get(git_submodule **, git_repository *, const char *, const char *);
72
+ static void submodule_release(git_submodule *sm, int decr);
73
+ static int submodule_load_from_index(git_repository *, const git_index_entry *);
74
+ static int submodule_load_from_head(git_repository*, const char*, const git_oid*);
75
+ static int submodule_load_from_config(const git_config_entry *, void *);
76
+ static int submodule_load_from_wd_lite(git_submodule *, const char *, void *);
77
+ static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
78
+ static void submodule_mode_mismatch(git_repository *, const char *, unsigned int);
79
+ static int submodule_index_status(unsigned int *status, git_submodule *sm);
80
+ static int submodule_wd_status(unsigned int *status, git_submodule *sm);
59
81
 
60
- static git_submodule *submodule_alloc(const char *name)
82
+ static int submodule_cmp(const void *a, const void *b)
61
83
  {
62
- git_submodule *sm = git__calloc(1, sizeof(git_submodule));
63
- if (sm == NULL)
64
- return sm;
65
-
66
- sm->path = sm->name = git__strdup(name);
67
- if (!sm->name) {
68
- git__free(sm);
69
- return NULL;
70
- }
84
+ return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
85
+ }
71
86
 
72
- return sm;
87
+ static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
88
+ {
89
+ ssize_t idx = git_buf_rfind(key, '.');
90
+ git_buf_truncate(key, (size_t)(idx + 1));
91
+ return git_buf_puts(key, suffix);
73
92
  }
74
93
 
75
- static void submodule_release(git_submodule *sm, int decr)
94
+ /*
95
+ * PUBLIC APIS
96
+ */
97
+
98
+ int git_submodule_lookup(
99
+ git_submodule **sm_ptr, /* NULL if user only wants to test existence */
100
+ git_repository *repo,
101
+ const char *name) /* trailing slash is allowed */
76
102
  {
77
- if (!sm)
78
- return;
103
+ int error;
104
+ khiter_t pos;
79
105
 
80
- sm->refcount -= decr;
106
+ assert(repo && name);
81
107
 
82
- if (sm->refcount == 0) {
83
- if (sm->name != sm->path)
84
- git__free(sm->path);
85
- git__free(sm->name);
86
- git__free(sm->url);
87
- git__free(sm);
108
+ if ((error = load_submodule_config(repo, false)) < 0)
109
+ return error;
110
+
111
+ pos = git_strmap_lookup_index(repo->submodules, name);
112
+
113
+ if (!git_strmap_valid_index(repo->submodules, pos)) {
114
+ error = GIT_ENOTFOUND;
115
+
116
+ /* check if a plausible submodule exists at path */
117
+ if (git_repository_workdir(repo)) {
118
+ git_buf path = GIT_BUF_INIT;
119
+
120
+ if (git_buf_joinpath(&path, git_repository_workdir(repo), name) < 0)
121
+ return -1;
122
+
123
+ if (git_path_contains_dir(&path, DOT_GIT))
124
+ error = GIT_EEXISTS;
125
+
126
+ git_buf_free(&path);
127
+ }
128
+
129
+ return error;
88
130
  }
131
+
132
+ if (sm_ptr)
133
+ *sm_ptr = git_strmap_value_at(repo->submodules, pos);
134
+
135
+ return 0;
89
136
  }
90
137
 
91
- static int submodule_from_entry(
92
- git_strmap *smcfg, git_index_entry *entry)
138
+ int git_submodule_foreach(
139
+ git_repository *repo,
140
+ int (*callback)(git_submodule *sm, const char *name, void *payload),
141
+ void *payload)
93
142
  {
94
- git_submodule *sm;
95
- void *old_sm;
96
- khiter_t pos;
97
143
  int error;
144
+ git_submodule *sm;
145
+ git_vector seen = GIT_VECTOR_INIT;
146
+ seen._cmp = submodule_cmp;
98
147
 
99
- pos = git_strmap_lookup_index(smcfg, entry->path);
100
-
101
- if (git_strmap_valid_index(smcfg, pos))
102
- sm = git_strmap_value_at(smcfg, pos);
103
- else
104
- sm = submodule_alloc(entry->path);
148
+ assert(repo && callback);
105
149
 
106
- git_oid_cpy(&sm->oid, &entry->oid);
150
+ if ((error = load_submodule_config(repo, false)) < 0)
151
+ return error;
107
152
 
108
- if (strcmp(sm->path, entry->path) != 0) {
109
- if (sm->path != sm->name) {
110
- git__free(sm->path);
111
- sm->path = sm->name;
153
+ git_strmap_foreach_value(repo->submodules, sm, {
154
+ /* Usually the following will not come into play - it just prevents
155
+ * us from issuing a callback twice for a submodule where the name
156
+ * and path are not the same.
157
+ */
158
+ if (sm->refcount > 1) {
159
+ if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
160
+ continue;
161
+ if ((error = git_vector_insert(&seen, sm)) < 0)
162
+ break;
112
163
  }
113
- sm->path = git__strdup(entry->path);
114
- if (!sm->path)
115
- goto fail;
116
- }
117
-
118
- git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
119
- if (error < 0)
120
- goto fail;
121
- sm->refcount++;
122
164
 
123
- if (old_sm && ((git_submodule *)old_sm) != sm) {
124
- /* TODO: log warning about multiple entrys for same submodule path */
125
- submodule_release(old_sm, 1);
126
- }
165
+ if (callback(sm, sm->name, payload)) {
166
+ giterr_clear();
167
+ error = GIT_EUSER;
168
+ break;
169
+ }
170
+ });
127
171
 
128
- return 0;
172
+ git_vector_free(&seen);
129
173
 
130
- fail:
131
- submodule_release(sm, 0);
132
- return -1;
174
+ return error;
133
175
  }
134
176
 
135
- static int submodule_from_config(
136
- const char *key, const char *value, void *data)
177
+ void git_submodule_config_free(git_repository *repo)
137
178
  {
138
- git_strmap *smcfg = data;
139
- const char *namestart;
140
- const char *property;
141
- git_buf name = GIT_BUF_INIT;
179
+ git_strmap *smcfg;
142
180
  git_submodule *sm;
143
- void *old_sm = NULL;
144
- bool is_path;
145
- khiter_t pos;
146
- int error;
147
181
 
148
- if (git__prefixcmp(key, "submodule.") != 0)
149
- return 0;
182
+ assert(repo);
150
183
 
151
- namestart = key + strlen("submodule.");
152
- property = strrchr(namestart, '.');
153
- if (property == NULL)
154
- return 0;
155
- property++;
156
- is_path = (strcmp(property, "path") == 0);
184
+ smcfg = repo->submodules;
185
+ repo->submodules = NULL;
157
186
 
158
- if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
159
- return -1;
187
+ if (smcfg == NULL)
188
+ return;
160
189
 
161
- pos = git_strmap_lookup_index(smcfg, name.ptr);
162
- if (!git_strmap_valid_index(smcfg, pos) && is_path)
163
- pos = git_strmap_lookup_index(smcfg, value);
164
- if (!git_strmap_valid_index(smcfg, pos))
165
- sm = submodule_alloc(name.ptr);
166
- else
167
- sm = git_strmap_value_at(smcfg, pos);
168
- if (!sm)
169
- goto fail;
190
+ git_strmap_foreach_value(smcfg, sm, {
191
+ submodule_release(sm,1);
192
+ });
193
+ git_strmap_free(smcfg);
194
+ }
170
195
 
171
- if (strcmp(sm->name, name.ptr) != 0) {
172
- assert(sm->path == sm->name);
173
- sm->name = git_buf_detach(&name);
196
+ int git_submodule_add_setup(
197
+ git_submodule **submodule,
198
+ git_repository *repo,
199
+ const char *url,
200
+ const char *path,
201
+ int use_gitlink)
202
+ {
203
+ int error = 0;
204
+ git_config_file *mods = NULL;
205
+ git_submodule *sm;
206
+ git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
207
+ git_repository_init_options initopt;
208
+ git_repository *subrepo = NULL;
174
209
 
175
- git_strmap_insert2(smcfg, sm->name, sm, old_sm, error);
176
- if (error < 0)
177
- goto fail;
178
- sm->refcount++;
179
- }
180
- else if (is_path && strcmp(sm->path, value) != 0) {
181
- assert(sm->path == sm->name);
182
- sm->path = git__strdup(value);
183
- if (sm->path == NULL)
184
- goto fail;
210
+ assert(repo && url && path);
185
211
 
186
- git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
187
- if (error < 0)
188
- goto fail;
189
- sm->refcount++;
190
- }
191
- git_buf_free(&name);
212
+ /* see if there is already an entry for this submodule */
192
213
 
193
- if (old_sm && ((git_submodule *)old_sm) != sm) {
194
- /* TODO: log warning about multiple submodules with same path */
195
- submodule_release(old_sm, 1);
214
+ if (git_submodule_lookup(&sm, repo, path) < 0)
215
+ giterr_clear();
216
+ else {
217
+ giterr_set(GITERR_SUBMODULE,
218
+ "Attempt to add a submodule that already exists");
219
+ return GIT_EEXISTS;
196
220
  }
197
221
 
198
- if (is_path)
199
- return 0;
222
+ /* resolve parameters */
200
223
 
201
- /* copy other properties into submodule entry */
202
- if (strcmp(property, "url") == 0) {
203
- if (sm->url) {
204
- git__free(sm->url);
205
- sm->url = NULL;
206
- }
207
- if ((sm->url = git__strdup(value)) == NULL)
208
- goto fail;
209
- }
210
- else if (strcmp(property, "update") == 0) {
211
- int val;
212
- if (git_config_lookup_map_value(
213
- _sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0) {
214
- giterr_set(GITERR_INVALID,
215
- "Invalid value for submodule update property: '%s'", value);
216
- goto fail;
217
- }
218
- sm->update = (git_submodule_update_t)val;
224
+ if (url[0] == '.' && (url[1] == '/' || (url[1] == '.' && url[2] == '/'))) {
225
+ if (!(error = lookup_head_remote(&real_url, repo)))
226
+ error = git_path_apply_relative(&real_url, url);
227
+ } else if (strchr(url, ':') != NULL || url[0] == '/') {
228
+ error = git_buf_sets(&real_url, url);
229
+ } else {
230
+ giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
231
+ error = -1;
219
232
  }
220
- else if (strcmp(property, "fetchRecurseSubmodules") == 0) {
221
- if (git__parse_bool(&sm->fetch_recurse, value) < 0) {
222
- giterr_set(GITERR_INVALID,
223
- "Invalid value for submodule 'fetchRecurseSubmodules' property: '%s'", value);
224
- goto fail;
225
- }
233
+ if (error)
234
+ goto cleanup;
235
+
236
+ /* validate and normalize path */
237
+
238
+ if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
239
+ path += strlen(git_repository_workdir(repo));
240
+
241
+ if (git_path_root(path) >= 0) {
242
+ giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path");
243
+ error = -1;
244
+ goto cleanup;
226
245
  }
227
- else if (strcmp(property, "ignore") == 0) {
228
- int val;
229
- if (git_config_lookup_map_value(
230
- _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0) {
231
- giterr_set(GITERR_INVALID,
232
- "Invalid value for submodule ignore property: '%s'", value);
233
- goto fail;
234
- }
235
- sm->ignore = (git_submodule_ignore_t)val;
246
+
247
+ /* update .gitmodules */
248
+
249
+ if ((mods = open_gitmodules(repo, true, NULL)) == NULL) {
250
+ giterr_set(GITERR_SUBMODULE,
251
+ "Adding submodules to a bare repository is not supported (for now)");
252
+ return -1;
236
253
  }
237
- /* ignore other unknown submodule properties */
238
254
 
239
- return 0;
255
+ if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
256
+ (error = git_config_file_set_string(mods, name.ptr, path)) < 0)
257
+ goto cleanup;
240
258
 
241
- fail:
242
- submodule_release(sm, 0);
243
- git_buf_free(&name);
244
- return -1;
245
- }
259
+ if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
260
+ (error = git_config_file_set_string(mods, name.ptr, real_url.ptr)) < 0)
261
+ goto cleanup;
246
262
 
247
- static int load_submodule_config(git_repository *repo)
248
- {
249
- int error;
250
- git_index *index;
251
- unsigned int i, max_i;
252
- git_oid gitmodules_oid;
253
- git_strmap *smcfg;
254
- struct git_config_file *mods = NULL;
263
+ git_buf_clear(&name);
255
264
 
256
- if (repo->submodules)
257
- return 0;
265
+ /* init submodule repository and add origin remote as needed */
266
+
267
+ error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
268
+ if (error < 0)
269
+ goto cleanup;
258
270
 
259
- /* submodule data is kept in a hashtable with each submodule stored
260
- * under both its name and its path. These are usually the same, but
261
- * that is not guaranteed.
271
+ /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
272
+ * gitlink in the sub-repo workdir directory to that repository
273
+ *
274
+ * Old style: sub-repo goes directly into repo/<name>/.git/
262
275
  */
263
- smcfg = git_strmap_alloc();
264
- GITERR_CHECK_ALLOC(smcfg);
265
276
 
266
- /* scan index for gitmodules (and .gitmodules entry) */
267
- if ((error = git_repository_index__weakptr(&index, repo)) < 0)
268
- goto cleanup;
269
- memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
270
- max_i = git_index_entrycount(index);
277
+ memset(&initopt, 0, sizeof(initopt));
278
+ initopt.flags = GIT_REPOSITORY_INIT_MKPATH |
279
+ GIT_REPOSITORY_INIT_NO_REINIT;
280
+ initopt.origin_url = real_url.ptr;
271
281
 
272
- for (i = 0; i < max_i; i++) {
273
- git_index_entry *entry = git_index_get(index, i);
274
- if (S_ISGITLINK(entry->mode)) {
275
- if ((error = submodule_from_entry(smcfg, entry)) < 0)
276
- goto cleanup;
277
- }
278
- else if (strcmp(entry->path, ".gitmodules") == 0)
279
- git_oid_cpy(&gitmodules_oid, &entry->oid);
282
+ if (git_path_exists(name.ptr) &&
283
+ git_path_contains(&name, DOT_GIT))
284
+ {
285
+ /* repo appears to already exist - reinit? */
280
286
  }
287
+ else if (use_gitlink) {
288
+ git_buf repodir = GIT_BUF_INIT;
281
289
 
282
- /* load .gitmodules from workdir if it exists */
283
- if (git_repository_workdir(repo) != NULL) {
284
- /* look in workdir for .gitmodules */
285
- git_buf path = GIT_BUF_INIT;
286
- if (!git_buf_joinpath(
287
- &path, git_repository_workdir(repo), ".gitmodules") &&
288
- git_path_isfile(path.ptr))
289
- {
290
- if (!(error = git_config_file__ondisk(&mods, path.ptr)))
291
- error = git_config_file_open(mods);
292
- }
293
- git_buf_free(&path);
294
- }
290
+ error = git_buf_join_n(
291
+ &repodir, '/', 3, git_repository_path(repo), "modules", path);
292
+ if (error < 0)
293
+ goto cleanup;
294
+
295
+ initopt.workdir_path = name.ptr;
296
+ initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
295
297
 
296
- /* load .gitmodules from object cache if not in workdir */
297
- if (!error && mods == NULL && !git_oid_iszero(&gitmodules_oid)) {
298
- /* TODO: is it worth loading gitmodules from object cache? */
298
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
299
+
300
+ git_buf_free(&repodir);
301
+ }
302
+ else {
303
+ error = git_repository_init_ext(&subrepo, name.ptr, &initopt);
299
304
  }
305
+ if (error < 0)
306
+ goto cleanup;
300
307
 
301
- /* process .gitmodules info */
302
- if (!error && mods != NULL)
303
- error = git_config_file_foreach(mods, submodule_from_config, smcfg);
308
+ /* add submodule to hash and "reload" it */
304
309
 
305
- /* store submodule config in repo */
306
- if (!error)
307
- repo->submodules = smcfg;
310
+ if (!(error = submodule_get(&sm, repo, path, NULL)) &&
311
+ !(error = git_submodule_reload(sm)))
312
+ error = git_submodule_init(sm, false);
308
313
 
309
314
  cleanup:
315
+ if (submodule != NULL)
316
+ *submodule = !error ? sm : NULL;
317
+
310
318
  if (mods != NULL)
311
319
  git_config_file_free(mods);
312
- if (error)
313
- git_strmap_free(smcfg);
320
+ git_repository_free(subrepo);
321
+ git_buf_free(&real_url);
322
+ git_buf_free(&name);
323
+
314
324
  return error;
315
325
  }
316
326
 
317
- void git_submodule_config_free(git_repository *repo)
327
+ int git_submodule_add_finalize(git_submodule *sm)
318
328
  {
319
- git_strmap *smcfg = repo->submodules;
320
- git_submodule *sm;
321
-
322
- repo->submodules = NULL;
329
+ int error;
330
+ git_index *index;
323
331
 
324
- if (smcfg == NULL)
325
- return;
332
+ assert(sm);
326
333
 
327
- git_strmap_foreach_value(smcfg, sm, {
328
- submodule_release(sm,1);
329
- });
330
- git_strmap_free(smcfg);
331
- }
334
+ if ((error = git_repository_index__weakptr(&index, sm->owner)) < 0 ||
335
+ (error = git_index_add(index, GIT_MODULES_FILE, 0)) < 0)
336
+ return error;
332
337
 
333
- static int submodule_cmp(const void *a, const void *b)
334
- {
335
- return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
338
+ return git_submodule_add_to_index(sm, true);
336
339
  }
337
340
 
338
- int git_submodule_foreach(
339
- git_repository *repo,
340
- int (*callback)(const char *name, void *payload),
341
- void *payload)
341
+ int git_submodule_add_to_index(git_submodule *sm, int write_index)
342
342
  {
343
343
  int error;
344
- git_submodule *sm;
345
- git_vector seen = GIT_VECTOR_INIT;
346
- seen._cmp = submodule_cmp;
344
+ git_repository *repo, *sm_repo;
345
+ git_index *index;
346
+ git_buf path = GIT_BUF_INIT;
347
+ git_commit *head;
348
+ git_index_entry entry;
349
+ struct stat st;
347
350
 
348
- if ((error = load_submodule_config(repo)) < 0)
349
- return error;
351
+ assert(sm);
350
352
 
351
- git_strmap_foreach_value(repo->submodules, sm, {
352
- /* usually the following will not come into play */
353
- if (sm->refcount > 1) {
354
- if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
355
- continue;
356
- if ((error = git_vector_insert(&seen, sm)) < 0)
357
- break;
358
- }
353
+ repo = sm->owner;
359
354
 
360
- if ((error = callback(sm->name, payload)) < 0)
361
- break;
362
- });
355
+ /* force reload of wd OID by git_submodule_open */
356
+ sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
363
357
 
364
- git_vector_free(&seen);
358
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
359
+ (error = git_buf_joinpath(
360
+ &path, git_repository_workdir(repo), sm->path)) < 0 ||
361
+ (error = git_submodule_open(&sm_repo, sm)) < 0)
362
+ goto cleanup;
363
+
364
+ /* read stat information for submodule working directory */
365
+ if (p_stat(path.ptr, &st) < 0) {
366
+ giterr_set(GITERR_SUBMODULE,
367
+ "Cannot add submodule without working directory");
368
+ error = -1;
369
+ goto cleanup;
370
+ }
371
+
372
+ memset(&entry, 0, sizeof(entry));
373
+ entry.path = sm->path;
374
+ git_index__init_entry_from_stat(&st, &entry);
375
+
376
+ /* calling git_submodule_open will have set sm->wd_oid if possible */
377
+ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
378
+ giterr_set(GITERR_SUBMODULE,
379
+ "Cannot add submodule without HEAD to index");
380
+ error = -1;
381
+ goto cleanup;
382
+ }
383
+ git_oid_cpy(&entry.oid, &sm->wd_oid);
384
+
385
+ if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0)
386
+ goto cleanup;
387
+
388
+ entry.ctime.seconds = git_commit_time(head);
389
+ entry.ctime.nanoseconds = 0;
390
+ entry.mtime.seconds = git_commit_time(head);
391
+ entry.mtime.nanoseconds = 0;
392
+
393
+ git_commit_free(head);
365
394
 
395
+ /* add it */
396
+ error = git_index_add2(index, &entry);
397
+
398
+ /* write it, if requested */
399
+ if (!error && write_index) {
400
+ error = git_index_write(index);
401
+
402
+ if (!error)
403
+ git_oid_cpy(&sm->index_oid, &sm->wd_oid);
404
+ }
405
+
406
+ cleanup:
407
+ git_repository_free(sm_repo);
408
+ git_buf_free(&path);
366
409
  return error;
367
410
  }
368
411
 
369
- int git_submodule_lookup(
370
- git_submodule **sm_ptr, /* NULL allowed if user only wants to test */
371
- git_repository *repo,
372
- const char *name) /* trailing slash is allowed */
412
+ int git_submodule_save(git_submodule *submodule)
373
413
  {
374
- khiter_t pos;
414
+ int error = 0;
415
+ git_config_file *mods;
416
+ git_buf key = GIT_BUF_INIT;
417
+
418
+ assert(submodule);
375
419
 
376
- if (load_submodule_config(repo) < 0)
420
+ mods = open_gitmodules(submodule->owner, true, NULL);
421
+ if (!mods) {
422
+ giterr_set(GITERR_SUBMODULE,
423
+ "Adding submodules to a bare repository is not supported (for now)");
377
424
  return -1;
425
+ }
378
426
 
379
- pos = git_strmap_lookup_index(repo->submodules, name);
380
- if (!git_strmap_valid_index(repo->submodules, pos))
381
- return GIT_ENOTFOUND;
427
+ if ((error = git_buf_printf(&key, "submodule.%s.", submodule->name)) < 0)
428
+ goto cleanup;
382
429
 
383
- if (sm_ptr)
384
- *sm_ptr = git_strmap_value_at(repo->submodules, pos);
430
+ /* save values for path, url, update, ignore, fetchRecurseSubmodules */
385
431
 
386
- return 0;
432
+ if ((error = submodule_config_key_trunc_puts(&key, "path")) < 0 ||
433
+ (error = git_config_file_set_string(mods, key.ptr, submodule->path)) < 0)
434
+ goto cleanup;
435
+
436
+ if ((error = submodule_config_key_trunc_puts(&key, "url")) < 0 ||
437
+ (error = git_config_file_set_string(mods, key.ptr, submodule->url)) < 0)
438
+ goto cleanup;
439
+
440
+ if (!(error = submodule_config_key_trunc_puts(&key, "update")) &&
441
+ submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
442
+ {
443
+ const char *val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
444
+ NULL : _sm_update_map[submodule->update].str_match;
445
+ error = git_config_file_set_string(mods, key.ptr, val);
446
+ }
447
+ if (error < 0)
448
+ goto cleanup;
449
+
450
+ if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) &&
451
+ submodule->ignore != GIT_SUBMODULE_IGNORE_DEFAULT)
452
+ {
453
+ const char *val = (submodule->ignore == GIT_SUBMODULE_IGNORE_NONE) ?
454
+ NULL : _sm_ignore_map[submodule->ignore].str_match;
455
+ error = git_config_file_set_string(mods, key.ptr, val);
456
+ }
457
+ if (error < 0)
458
+ goto cleanup;
459
+
460
+ if ((error = submodule_config_key_trunc_puts(
461
+ &key, "fetchRecurseSubmodules")) < 0 ||
462
+ (error = git_config_file_set_string(
463
+ mods, key.ptr, submodule->fetch_recurse ? "true" : "false")) < 0)
464
+ goto cleanup;
465
+
466
+ /* update internal defaults */
467
+
468
+ submodule->ignore_default = submodule->ignore;
469
+ submodule->update_default = submodule->update;
470
+ submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
471
+
472
+ cleanup:
473
+ if (mods != NULL)
474
+ git_config_file_free(mods);
475
+ git_buf_free(&key);
476
+
477
+ return error;
478
+ }
479
+
480
+ git_repository *git_submodule_owner(git_submodule *submodule)
481
+ {
482
+ assert(submodule);
483
+ return submodule->owner;
484
+ }
485
+
486
+ const char *git_submodule_name(git_submodule *submodule)
487
+ {
488
+ assert(submodule);
489
+ return submodule->name;
490
+ }
491
+
492
+ const char *git_submodule_path(git_submodule *submodule)
493
+ {
494
+ assert(submodule);
495
+ return submodule->path;
496
+ }
497
+
498
+ const char *git_submodule_url(git_submodule *submodule)
499
+ {
500
+ assert(submodule);
501
+ return submodule->url;
502
+ }
503
+
504
+ int git_submodule_set_url(git_submodule *submodule, const char *url)
505
+ {
506
+ assert(submodule && url);
507
+
508
+ git__free(submodule->url);
509
+
510
+ submodule->url = git__strdup(url);
511
+ GITERR_CHECK_ALLOC(submodule->url);
512
+
513
+ return 0;
514
+ }
515
+
516
+ const git_oid *git_submodule_index_oid(git_submodule *submodule)
517
+ {
518
+ assert(submodule);
519
+
520
+ if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
521
+ return &submodule->index_oid;
522
+ else
523
+ return NULL;
524
+ }
525
+
526
+ const git_oid *git_submodule_head_oid(git_submodule *submodule)
527
+ {
528
+ assert(submodule);
529
+
530
+ if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
531
+ return &submodule->head_oid;
532
+ else
533
+ return NULL;
534
+ }
535
+
536
+ const git_oid *git_submodule_wd_oid(git_submodule *submodule)
537
+ {
538
+ assert(submodule);
539
+
540
+ if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
541
+ git_repository *subrepo;
542
+
543
+ /* calling submodule open grabs the HEAD OID if possible */
544
+ if (!git_submodule_open(&subrepo, submodule))
545
+ git_repository_free(subrepo);
546
+ else
547
+ giterr_clear();
548
+ }
549
+
550
+ if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
551
+ return &submodule->wd_oid;
552
+ else
553
+ return NULL;
554
+ }
555
+
556
+ git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
557
+ {
558
+ assert(submodule);
559
+ return submodule->ignore;
560
+ }
561
+
562
+ git_submodule_ignore_t git_submodule_set_ignore(
563
+ git_submodule *submodule, git_submodule_ignore_t ignore)
564
+ {
565
+ git_submodule_ignore_t old;
566
+
567
+ assert(submodule);
568
+
569
+ if (ignore == GIT_SUBMODULE_IGNORE_DEFAULT)
570
+ ignore = submodule->ignore_default;
571
+
572
+ old = submodule->ignore;
573
+ submodule->ignore = ignore;
574
+ return old;
575
+ }
576
+
577
+ git_submodule_update_t git_submodule_update(git_submodule *submodule)
578
+ {
579
+ assert(submodule);
580
+ return submodule->update;
581
+ }
582
+
583
+ git_submodule_update_t git_submodule_set_update(
584
+ git_submodule *submodule, git_submodule_update_t update)
585
+ {
586
+ git_submodule_update_t old;
587
+
588
+ assert(submodule);
589
+
590
+ if (update == GIT_SUBMODULE_UPDATE_DEFAULT)
591
+ update = submodule->update_default;
592
+
593
+ old = submodule->update;
594
+ submodule->update = update;
595
+ return old;
596
+ }
597
+
598
+ int git_submodule_fetch_recurse_submodules(
599
+ git_submodule *submodule)
600
+ {
601
+ assert(submodule);
602
+ return submodule->fetch_recurse;
603
+ }
604
+
605
+ int git_submodule_set_fetch_recurse_submodules(
606
+ git_submodule *submodule,
607
+ int fetch_recurse_submodules)
608
+ {
609
+ int old;
610
+
611
+ assert(submodule);
612
+
613
+ old = submodule->fetch_recurse;
614
+ submodule->fetch_recurse = (fetch_recurse_submodules != 0);
615
+ return old;
616
+ }
617
+
618
+ int git_submodule_init(git_submodule *submodule, int overwrite)
619
+ {
620
+ int error;
621
+
622
+ /* write "submodule.NAME.url" */
623
+
624
+ if (!submodule->url) {
625
+ giterr_set(GITERR_SUBMODULE,
626
+ "No URL configured for submodule '%s'", submodule->name);
627
+ return -1;
628
+ }
629
+
630
+ error = submodule_update_config(
631
+ submodule, "url", submodule->url, overwrite != 0, false);
632
+ if (error < 0)
633
+ return error;
634
+
635
+ /* write "submodule.NAME.update" if not default */
636
+
637
+ if (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT)
638
+ error = submodule_update_config(
639
+ submodule, "update", NULL, (overwrite != 0), false);
640
+ else if (submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
641
+ error = submodule_update_config(
642
+ submodule, "update",
643
+ _sm_update_map[submodule->update].str_match,
644
+ (overwrite != 0), false);
645
+
646
+ return error;
647
+ }
648
+
649
+ int git_submodule_sync(git_submodule *submodule)
650
+ {
651
+ if (!submodule->url) {
652
+ giterr_set(GITERR_SUBMODULE,
653
+ "No URL configured for submodule '%s'", submodule->name);
654
+ return -1;
655
+ }
656
+
657
+ /* copy URL over to config only if it already exists */
658
+
659
+ return submodule_update_config(
660
+ submodule, "url", submodule->url, true, true);
661
+ }
662
+
663
+ int git_submodule_open(
664
+ git_repository **subrepo,
665
+ git_submodule *submodule)
666
+ {
667
+ int error;
668
+ git_buf path = GIT_BUF_INIT;
669
+ git_repository *repo;
670
+ const char *workdir;
671
+
672
+ assert(submodule && subrepo);
673
+
674
+ repo = submodule->owner;
675
+ workdir = git_repository_workdir(repo);
676
+
677
+ if (!workdir) {
678
+ giterr_set(GITERR_REPOSITORY,
679
+ "Cannot open submodule repository in a bare repo");
680
+ return GIT_ENOTFOUND;
681
+ }
682
+
683
+ if ((submodule->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0) {
684
+ giterr_set(GITERR_REPOSITORY,
685
+ "Cannot open submodule repository that is not checked out");
686
+ return GIT_ENOTFOUND;
687
+ }
688
+
689
+ if (git_buf_joinpath(&path, workdir, submodule->path) < 0)
690
+ return -1;
691
+
692
+ error = git_repository_open(subrepo, path.ptr);
693
+
694
+ git_buf_free(&path);
695
+
696
+ /* if we have opened the submodule successfully, let's grab the HEAD OID */
697
+ if (!error && !(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
698
+ if (!git_reference_name_to_oid(
699
+ &submodule->wd_oid, *subrepo, GIT_HEAD_FILE))
700
+ submodule->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
701
+ else
702
+ giterr_clear();
703
+ }
704
+
705
+ return error;
706
+ }
707
+
708
+ int git_submodule_reload_all(git_repository *repo)
709
+ {
710
+ assert(repo);
711
+ return load_submodule_config(repo, true);
712
+ }
713
+
714
+ int git_submodule_reload(git_submodule *submodule)
715
+ {
716
+ git_repository *repo;
717
+ git_index *index;
718
+ int pos, error;
719
+ git_tree *head;
720
+ git_config_file *mods;
721
+
722
+ assert(submodule);
723
+
724
+ /* refresh index data */
725
+
726
+ repo = submodule->owner;
727
+ if (git_repository_index__weakptr(&index, repo) < 0)
728
+ return -1;
729
+
730
+ submodule->flags = submodule->flags &
731
+ ~(GIT_SUBMODULE_STATUS_IN_INDEX |
732
+ GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
733
+
734
+ pos = git_index_find(index, submodule->path);
735
+ if (pos >= 0) {
736
+ git_index_entry *entry = git_index_get(index, pos);
737
+
738
+ if (S_ISGITLINK(entry->mode)) {
739
+ if ((error = submodule_load_from_index(repo, entry)) < 0)
740
+ return error;
741
+ } else {
742
+ submodule_mode_mismatch(
743
+ repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
744
+ }
745
+ }
746
+
747
+ /* refresh HEAD tree data */
748
+
749
+ if (!(error = git_repository_head_tree(&head, repo))) {
750
+ git_tree_entry *te;
751
+
752
+ submodule->flags = submodule->flags &
753
+ ~(GIT_SUBMODULE_STATUS_IN_HEAD |
754
+ GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
755
+
756
+ if (!(error = git_tree_entry_bypath(&te, head, submodule->path))) {
757
+
758
+ if (S_ISGITLINK(te->attr)) {
759
+ error = submodule_load_from_head(repo, submodule->path, &te->oid);
760
+ } else {
761
+ submodule_mode_mismatch(
762
+ repo, submodule->path,
763
+ GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
764
+ }
765
+
766
+ git_tree_entry_free(te);
767
+ }
768
+ else if (error == GIT_ENOTFOUND) {
769
+ giterr_clear();
770
+ error = 0;
771
+ }
772
+
773
+ git_tree_free(head);
774
+ }
775
+
776
+ if (error < 0)
777
+ return error;
778
+
779
+ /* refresh config data */
780
+
781
+ if ((mods = open_gitmodules(repo, false, NULL)) != NULL) {
782
+ git_buf path = GIT_BUF_INIT;
783
+
784
+ git_buf_sets(&path, "submodule\\.");
785
+ git_buf_puts_escape_regex(&path, submodule->name);
786
+ git_buf_puts(&path, ".*");
787
+
788
+ if (git_buf_oom(&path))
789
+ error = -1;
790
+ else
791
+ error = git_config_file_foreach_match(
792
+ mods, path.ptr, submodule_load_from_config, repo);
793
+
794
+ git_buf_free(&path);
795
+ git_config_file_free(mods);
796
+ }
797
+
798
+ if (error < 0)
799
+ return error;
800
+
801
+ /* refresh wd data */
802
+
803
+ submodule->flags = submodule->flags &
804
+ ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID);
805
+
806
+ error = submodule_load_from_wd_lite(submodule, submodule->path, NULL);
807
+
808
+ return error;
809
+ }
810
+
811
+ int git_submodule_status(
812
+ unsigned int *status,
813
+ git_submodule *submodule)
814
+ {
815
+ int error = 0;
816
+ unsigned int status_val;
817
+
818
+ assert(status && submodule);
819
+
820
+ status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
821
+
822
+ if (submodule->ignore != GIT_SUBMODULE_IGNORE_ALL) {
823
+ if (!(error = submodule_index_status(&status_val, submodule)))
824
+ error = submodule_wd_status(&status_val, submodule);
825
+ }
826
+
827
+ *status = status_val;
828
+
829
+ return error;
830
+ }
831
+
832
+ /*
833
+ * INTERNAL FUNCTIONS
834
+ */
835
+
836
+ static git_submodule *submodule_alloc(git_repository *repo, const char *name)
837
+ {
838
+ git_submodule *sm;
839
+
840
+ if (!name || !strlen(name)) {
841
+ giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
842
+ return NULL;
843
+ }
844
+
845
+ sm = git__calloc(1, sizeof(git_submodule));
846
+ if (sm == NULL)
847
+ goto fail;
848
+
849
+ sm->path = sm->name = git__strdup(name);
850
+ if (!sm->name)
851
+ goto fail;
852
+
853
+ sm->owner = repo;
854
+ sm->refcount = 1;
855
+
856
+ return sm;
857
+
858
+ fail:
859
+ submodule_release(sm, 0);
860
+ return NULL;
861
+ }
862
+
863
+ static void submodule_release(git_submodule *sm, int decr)
864
+ {
865
+ if (!sm)
866
+ return;
867
+
868
+ sm->refcount -= decr;
869
+
870
+ if (sm->refcount == 0) {
871
+ if (sm->name != sm->path) {
872
+ git__free(sm->path);
873
+ sm->path = NULL;
874
+ }
875
+
876
+ git__free(sm->name);
877
+ sm->name = NULL;
878
+
879
+ git__free(sm->url);
880
+ sm->url = NULL;
881
+
882
+ sm->owner = NULL;
883
+
884
+ git__free(sm);
885
+ }
886
+ }
887
+
888
+ static int submodule_get(
889
+ git_submodule **sm_ptr,
890
+ git_repository *repo,
891
+ const char *name,
892
+ const char *alternate)
893
+ {
894
+ git_strmap *smcfg = repo->submodules;
895
+ khiter_t pos;
896
+ git_submodule *sm;
897
+ int error;
898
+
899
+ assert(repo && name);
900
+
901
+ pos = git_strmap_lookup_index(smcfg, name);
902
+
903
+ if (!git_strmap_valid_index(smcfg, pos) && alternate)
904
+ pos = git_strmap_lookup_index(smcfg, alternate);
905
+
906
+ if (!git_strmap_valid_index(smcfg, pos)) {
907
+ sm = submodule_alloc(repo, name);
908
+
909
+ /* insert value at name - if another thread beats us to it, then use
910
+ * their record and release our own.
911
+ */
912
+ pos = kh_put(str, smcfg, sm->name, &error);
913
+
914
+ if (error < 0) {
915
+ submodule_release(sm, 1);
916
+ sm = NULL;
917
+ } else if (error == 0) {
918
+ submodule_release(sm, 1);
919
+ sm = git_strmap_value_at(smcfg, pos);
920
+ } else {
921
+ git_strmap_set_value_at(smcfg, pos, sm);
922
+ }
923
+ } else {
924
+ sm = git_strmap_value_at(smcfg, pos);
925
+ }
926
+
927
+ *sm_ptr = sm;
928
+
929
+ return (sm != NULL) ? 0 : -1;
930
+ }
931
+
932
+ static int submodule_load_from_index(
933
+ git_repository *repo, const git_index_entry *entry)
934
+ {
935
+ git_submodule *sm;
936
+
937
+ if (submodule_get(&sm, repo, entry->path, NULL) < 0)
938
+ return -1;
939
+
940
+ if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
941
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
942
+ return 0;
943
+ }
944
+
945
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
946
+
947
+ git_oid_cpy(&sm->index_oid, &entry->oid);
948
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
949
+
950
+ return 0;
951
+ }
952
+
953
+ static int submodule_load_from_head(
954
+ git_repository *repo, const char *path, const git_oid *oid)
955
+ {
956
+ git_submodule *sm;
957
+
958
+ if (submodule_get(&sm, repo, path, NULL) < 0)
959
+ return -1;
960
+
961
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
962
+
963
+ git_oid_cpy(&sm->head_oid, oid);
964
+ sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
965
+
966
+ return 0;
967
+ }
968
+
969
+ static int submodule_config_error(const char *property, const char *value)
970
+ {
971
+ giterr_set(GITERR_INVALID,
972
+ "Invalid value for submodule '%s' property: '%s'", property, value);
973
+ return -1;
974
+ }
975
+
976
+ static int submodule_load_from_config(
977
+ const git_config_entry *entry, void *data)
978
+ {
979
+ git_repository *repo = data;
980
+ git_strmap *smcfg = repo->submodules;
981
+ const char *namestart, *property, *alternate = NULL;
982
+ const char *key = entry->name, *value = entry->value;
983
+ git_buf name = GIT_BUF_INIT;
984
+ git_submodule *sm;
985
+ bool is_path;
986
+ int error = 0;
987
+
988
+ if (git__prefixcmp(key, "submodule.") != 0)
989
+ return 0;
990
+
991
+ namestart = key + strlen("submodule.");
992
+ property = strrchr(namestart, '.');
993
+ if (property == NULL)
994
+ return 0;
995
+ property++;
996
+ is_path = (strcasecmp(property, "path") == 0);
997
+
998
+ if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
999
+ return -1;
1000
+
1001
+ if (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) {
1002
+ git_buf_free(&name);
1003
+ return -1;
1004
+ }
1005
+
1006
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
1007
+
1008
+ /* Only from config might we get differing names & paths. If so, then
1009
+ * update the submodule and insert under the alternative key.
1010
+ */
1011
+
1012
+ /* TODO: if case insensitive filesystem, then the following strcmps
1013
+ * should be strcasecmp
1014
+ */
1015
+
1016
+ if (strcmp(sm->name, name.ptr) != 0) {
1017
+ alternate = sm->name = git_buf_detach(&name);
1018
+ } else if (is_path && value && strcmp(sm->path, value) != 0) {
1019
+ alternate = sm->path = git__strdup(value);
1020
+ if (!sm->path)
1021
+ error = -1;
1022
+ }
1023
+ if (alternate) {
1024
+ void *old_sm = NULL;
1025
+ git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
1026
+
1027
+ if (error >= 0)
1028
+ sm->refcount++; /* inserted under a new key */
1029
+
1030
+ /* if we replaced an old module under this key, release the old one */
1031
+ if (old_sm && ((git_submodule *)old_sm) != sm) {
1032
+ submodule_release(old_sm, 1);
1033
+ /* TODO: log warning about multiple submodules with same path */
1034
+ }
1035
+ }
1036
+
1037
+ git_buf_free(&name);
1038
+ if (error < 0)
1039
+ return error;
1040
+
1041
+ /* TODO: Look up path in index and if it is present but not a GITLINK
1042
+ * then this should be deleted (at least to match git's behavior)
1043
+ */
1044
+
1045
+ if (is_path)
1046
+ return 0;
1047
+
1048
+ /* copy other properties into submodule entry */
1049
+ if (strcasecmp(property, "url") == 0) {
1050
+ git__free(sm->url);
1051
+ sm->url = NULL;
1052
+
1053
+ if (value != NULL && (sm->url = git__strdup(value)) == NULL)
1054
+ return -1;
1055
+ }
1056
+ else if (strcasecmp(property, "update") == 0) {
1057
+ int val;
1058
+ if (git_config_lookup_map_value(
1059
+ &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0)
1060
+ return submodule_config_error("update", value);
1061
+ sm->update_default = sm->update = (git_submodule_update_t)val;
1062
+ }
1063
+ else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
1064
+ if (git__parse_bool(&sm->fetch_recurse, value) < 0)
1065
+ return submodule_config_error("fetchRecurseSubmodules", value);
1066
+ }
1067
+ else if (strcasecmp(property, "ignore") == 0) {
1068
+ int val;
1069
+ if (git_config_lookup_map_value(
1070
+ &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0)
1071
+ return submodule_config_error("ignore", value);
1072
+ sm->ignore_default = sm->ignore = (git_submodule_ignore_t)val;
1073
+ }
1074
+ /* ignore other unknown submodule properties */
1075
+
1076
+ return 0;
1077
+ }
1078
+
1079
+ static int submodule_load_from_wd_lite(
1080
+ git_submodule *sm, const char *name, void *payload)
1081
+ {
1082
+ git_repository *repo = git_submodule_owner(sm);
1083
+ git_buf path = GIT_BUF_INIT;
1084
+
1085
+ GIT_UNUSED(name);
1086
+ GIT_UNUSED(payload);
1087
+
1088
+ if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
1089
+ return -1;
1090
+
1091
+ if (git_path_isdir(path.ptr))
1092
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
1093
+
1094
+ if (git_path_contains(&path, DOT_GIT))
1095
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
1096
+
1097
+ git_buf_free(&path);
1098
+
1099
+ return 0;
1100
+ }
1101
+
1102
+ static void submodule_mode_mismatch(
1103
+ git_repository *repo, const char *path, unsigned int flag)
1104
+ {
1105
+ khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
1106
+
1107
+ if (git_strmap_valid_index(repo->submodules, pos)) {
1108
+ git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
1109
+
1110
+ sm->flags |= flag;
1111
+ }
1112
+ }
1113
+
1114
+ static int load_submodule_config_from_index(
1115
+ git_repository *repo, git_oid *gitmodules_oid)
1116
+ {
1117
+ int error;
1118
+ git_iterator *i;
1119
+ const git_index_entry *entry;
1120
+
1121
+ if ((error = git_iterator_for_index(&i, repo)) < 0)
1122
+ return error;
1123
+
1124
+ error = git_iterator_current(i, &entry);
1125
+
1126
+ while (!error && entry != NULL) {
1127
+
1128
+ if (S_ISGITLINK(entry->mode)) {
1129
+ error = submodule_load_from_index(repo, entry);
1130
+ if (error < 0)
1131
+ break;
1132
+ } else {
1133
+ submodule_mode_mismatch(
1134
+ repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
1135
+
1136
+ if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
1137
+ git_oid_cpy(gitmodules_oid, &entry->oid);
1138
+ }
1139
+
1140
+ error = git_iterator_advance(i, &entry);
1141
+ }
1142
+
1143
+ git_iterator_free(i);
1144
+
1145
+ return error;
1146
+ }
1147
+
1148
+ static int load_submodule_config_from_head(
1149
+ git_repository *repo, git_oid *gitmodules_oid)
1150
+ {
1151
+ int error;
1152
+ git_tree *head;
1153
+ git_iterator *i;
1154
+ const git_index_entry *entry;
1155
+
1156
+ if ((error = git_repository_head_tree(&head, repo)) < 0)
1157
+ return error;
1158
+
1159
+ if ((error = git_iterator_for_tree(&i, repo, head)) < 0) {
1160
+ git_tree_free(head);
1161
+ return error;
1162
+ }
1163
+
1164
+ error = git_iterator_current(i, &entry);
1165
+
1166
+ while (!error && entry != NULL) {
1167
+
1168
+ if (S_ISGITLINK(entry->mode)) {
1169
+ error = submodule_load_from_head(repo, entry->path, &entry->oid);
1170
+ if (error < 0)
1171
+ break;
1172
+ } else {
1173
+ submodule_mode_mismatch(
1174
+ repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
1175
+
1176
+ if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
1177
+ git_oid_iszero(gitmodules_oid))
1178
+ git_oid_cpy(gitmodules_oid, &entry->oid);
1179
+ }
1180
+
1181
+ error = git_iterator_advance(i, &entry);
1182
+ }
1183
+
1184
+ git_iterator_free(i);
1185
+ git_tree_free(head);
1186
+
1187
+ return error;
1188
+ }
1189
+
1190
+ static git_config_file *open_gitmodules(
1191
+ git_repository *repo,
1192
+ bool okay_to_create,
1193
+ const git_oid *gitmodules_oid)
1194
+ {
1195
+ const char *workdir = git_repository_workdir(repo);
1196
+ git_buf path = GIT_BUF_INIT;
1197
+ git_config_file *mods = NULL;
1198
+
1199
+ if (workdir != NULL) {
1200
+ if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
1201
+ return NULL;
1202
+
1203
+ if (okay_to_create || git_path_isfile(path.ptr)) {
1204
+ /* git_config_file__ondisk should only fail if OOM */
1205
+ if (git_config_file__ondisk(&mods, path.ptr) < 0)
1206
+ mods = NULL;
1207
+ /* open should only fail here if the file is malformed */
1208
+ else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
1209
+ git_config_file_free(mods);
1210
+ mods = NULL;
1211
+ }
1212
+ }
1213
+ }
1214
+
1215
+ if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) {
1216
+ /* TODO: Retrieve .gitmodules content from ODB */
1217
+
1218
+ /* Should we actually do this? Core git does not, but it means you
1219
+ * can't really get much information about submodules on bare repos.
1220
+ */
1221
+ }
1222
+
1223
+ git_buf_free(&path);
1224
+
1225
+ return mods;
1226
+ }
1227
+
1228
+ static int load_submodule_config(git_repository *repo, bool force)
1229
+ {
1230
+ int error;
1231
+ git_oid gitmodules_oid;
1232
+ git_buf path = GIT_BUF_INIT;
1233
+ git_config_file *mods = NULL;
1234
+
1235
+ if (repo->submodules && !force)
1236
+ return 0;
1237
+
1238
+ memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
1239
+
1240
+ /* Submodule data is kept in a hashtable keyed by both name and path.
1241
+ * These are usually the same, but that is not guaranteed.
1242
+ */
1243
+ if (!repo->submodules) {
1244
+ repo->submodules = git_strmap_alloc();
1245
+ GITERR_CHECK_ALLOC(repo->submodules);
1246
+ }
1247
+
1248
+ /* add submodule information from index */
1249
+
1250
+ if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0)
1251
+ goto cleanup;
1252
+
1253
+ /* add submodule information from HEAD */
1254
+
1255
+ if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0)
1256
+ goto cleanup;
1257
+
1258
+ /* add submodule information from .gitmodules */
1259
+
1260
+ if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL)
1261
+ error = git_config_file_foreach(mods, submodule_load_from_config, repo);
1262
+
1263
+ if (error != 0)
1264
+ goto cleanup;
1265
+
1266
+ /* shallow scan submodules in work tree */
1267
+
1268
+ if (!git_repository_is_bare(repo))
1269
+ error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL);
1270
+
1271
+ cleanup:
1272
+ git_buf_free(&path);
1273
+
1274
+ if (mods != NULL)
1275
+ git_config_file_free(mods);
1276
+
1277
+ if (error)
1278
+ git_submodule_config_free(repo);
1279
+
1280
+ return error;
1281
+ }
1282
+
1283
+ static int lookup_head_remote(git_buf *url, git_repository *repo)
1284
+ {
1285
+ int error;
1286
+ git_config *cfg;
1287
+ git_reference *head = NULL, *remote = NULL;
1288
+ const char *tgt, *scan;
1289
+ git_buf key = GIT_BUF_INIT;
1290
+
1291
+ /* 1. resolve HEAD -> refs/heads/BRANCH
1292
+ * 2. lookup config branch.BRANCH.remote -> ORIGIN
1293
+ * 3. lookup remote.ORIGIN.url
1294
+ */
1295
+
1296
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1297
+ return error;
1298
+
1299
+ if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
1300
+ giterr_set(GITERR_SUBMODULE,
1301
+ "Cannot resolve relative URL when HEAD cannot be resolved");
1302
+ error = GIT_ENOTFOUND;
1303
+ goto cleanup;
1304
+ }
1305
+
1306
+ if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
1307
+ giterr_set(GITERR_SUBMODULE,
1308
+ "Cannot resolve relative URL when HEAD is not symbolic");
1309
+ error = GIT_ENOTFOUND;
1310
+ goto cleanup;
1311
+ }
1312
+
1313
+ if ((error = git_branch_tracking(&remote, head)) < 0)
1314
+ goto cleanup;
1315
+
1316
+ /* remote should refer to something like refs/remotes/ORIGIN/BRANCH */
1317
+
1318
+ if (git_reference_type(remote) != GIT_REF_SYMBOLIC ||
1319
+ git__prefixcmp(git_reference_target(remote), GIT_REFS_REMOTES_DIR) != 0)
1320
+ {
1321
+ giterr_set(GITERR_SUBMODULE,
1322
+ "Cannot resolve relative URL when HEAD is not symbolic");
1323
+ error = GIT_ENOTFOUND;
1324
+ goto cleanup;
1325
+ }
1326
+
1327
+ scan = tgt = git_reference_target(remote) + strlen(GIT_REFS_REMOTES_DIR);
1328
+ while (*scan && (*scan != '/' || (scan > tgt && scan[-1] != '\\')))
1329
+ scan++; /* find non-escaped slash to end ORIGIN name */
1330
+
1331
+ error = git_buf_printf(&key, "remote.%.*s.url", (int)(scan - tgt), tgt);
1332
+ if (error < 0)
1333
+ goto cleanup;
1334
+
1335
+ if ((error = git_config_get_string(&tgt, cfg, key.ptr)) < 0)
1336
+ goto cleanup;
1337
+
1338
+ error = git_buf_sets(url, tgt);
1339
+
1340
+ cleanup:
1341
+ git_buf_free(&key);
1342
+ git_reference_free(head);
1343
+ git_reference_free(remote);
1344
+
1345
+ return error;
1346
+ }
1347
+
1348
+ static int submodule_update_config(
1349
+ git_submodule *submodule,
1350
+ const char *attr,
1351
+ const char *value,
1352
+ bool overwrite,
1353
+ bool only_existing)
1354
+ {
1355
+ int error;
1356
+ git_config *config;
1357
+ git_buf key = GIT_BUF_INIT;
1358
+ const char *old = NULL;
1359
+
1360
+ assert(submodule);
1361
+
1362
+ error = git_repository_config__weakptr(&config, submodule->owner);
1363
+ if (error < 0)
1364
+ return error;
1365
+
1366
+ error = git_buf_printf(&key, "submodule.%s.%s", submodule->name, attr);
1367
+ if (error < 0)
1368
+ goto cleanup;
1369
+
1370
+ if (git_config_get_string(&old, config, key.ptr) < 0)
1371
+ giterr_clear();
1372
+
1373
+ if (!old && only_existing)
1374
+ goto cleanup;
1375
+ if (old && !overwrite)
1376
+ goto cleanup;
1377
+ if ((!old && !value) || (old && value && strcmp(old, value) == 0))
1378
+ goto cleanup;
1379
+
1380
+ if (!value)
1381
+ error = git_config_delete(config, key.ptr);
1382
+ else
1383
+ error = git_config_set_string(config, key.ptr, value);
1384
+
1385
+ cleanup:
1386
+ git_buf_free(&key);
1387
+ return error;
1388
+ }
1389
+
1390
+ static int submodule_index_status(unsigned int *status, git_submodule *sm)
1391
+ {
1392
+ const git_oid *head_oid = git_submodule_head_oid(sm);
1393
+ const git_oid *index_oid = git_submodule_index_oid(sm);
1394
+
1395
+ if (!head_oid) {
1396
+ if (index_oid)
1397
+ *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
1398
+ }
1399
+ else if (!index_oid)
1400
+ *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
1401
+ else if (!git_oid_equal(head_oid, index_oid))
1402
+ *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
1403
+
1404
+ return 0;
1405
+ }
1406
+
1407
+ static int submodule_wd_status(unsigned int *status, git_submodule *sm)
1408
+ {
1409
+ int error = 0;
1410
+ const git_oid *wd_oid, *index_oid;
1411
+ git_repository *sm_repo = NULL;
1412
+
1413
+ /* open repo now if we need it (so wd_oid() call won't reopen) */
1414
+ if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
1415
+ sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
1416
+ (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
1417
+ {
1418
+ if ((error = git_submodule_open(&sm_repo, sm)) < 0)
1419
+ return error;
1420
+ }
1421
+
1422
+ index_oid = git_submodule_index_oid(sm);
1423
+ wd_oid = git_submodule_wd_oid(sm);
1424
+
1425
+ if (!index_oid) {
1426
+ if (wd_oid)
1427
+ *status |= GIT_SUBMODULE_STATUS_WD_ADDED;
1428
+ }
1429
+ else if (!wd_oid) {
1430
+ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
1431
+ (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
1432
+ *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
1433
+ else
1434
+ *status |= GIT_SUBMODULE_STATUS_WD_DELETED;
1435
+ }
1436
+ else if (!git_oid_equal(index_oid, wd_oid))
1437
+ *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
1438
+
1439
+ if (sm_repo != NULL) {
1440
+ git_tree *sm_head;
1441
+ git_diff_options opt;
1442
+ git_diff_list *diff;
1443
+
1444
+ /* the diffs below could be optimized with an early termination
1445
+ * option to the git_diff functions, but for now this is sufficient
1446
+ * (and certainly no worse that what core git does).
1447
+ */
1448
+
1449
+ /* perform head-to-index diff on submodule */
1450
+
1451
+ if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
1452
+ return error;
1453
+
1454
+ memset(&opt, 0, sizeof(opt));
1455
+ if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
1456
+ opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
1457
+
1458
+ error = git_diff_index_to_tree(sm_repo, &opt, sm_head, &diff);
1459
+
1460
+ if (!error) {
1461
+ if (git_diff_num_deltas(diff) > 0)
1462
+ *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
1463
+
1464
+ git_diff_list_free(diff);
1465
+ diff = NULL;
1466
+ }
1467
+
1468
+ git_tree_free(sm_head);
1469
+
1470
+ if (error < 0)
1471
+ return error;
1472
+
1473
+ /* perform index-to-workdir diff on submodule */
1474
+
1475
+ error = git_diff_workdir_to_index(sm_repo, &opt, &diff);
1476
+
1477
+ if (!error) {
1478
+ size_t untracked =
1479
+ git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
1480
+
1481
+ if (untracked > 0)
1482
+ *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
1483
+
1484
+ if ((git_diff_num_deltas(diff) - untracked) > 0)
1485
+ *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
1486
+
1487
+ git_diff_list_free(diff);
1488
+ diff = NULL;
1489
+ }
1490
+
1491
+ git_repository_free(sm_repo);
1492
+ }
1493
+
1494
+ return error;
387
1495
  }