rugged 0.22.0b5 → 0.22.1b1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -3
  3. data/ext/rugged/extconf.rb +21 -5
  4. data/ext/rugged/rugged.c +1 -0
  5. data/ext/rugged/rugged.h +8 -0
  6. data/ext/rugged/rugged_backend.c +34 -0
  7. data/ext/rugged/rugged_branch_collection.c +1 -0
  8. data/ext/rugged/rugged_remote.c +37 -86
  9. data/ext/rugged/rugged_remote_collection.c +2 -1
  10. data/ext/rugged/rugged_repo.c +149 -24
  11. data/ext/rugged/rugged_revwalk.c +1 -2
  12. data/ext/rugged/rugged_submodule.c +1 -1
  13. data/ext/rugged/rugged_tree.c +69 -5
  14. data/lib/rugged/version.rb +1 -1
  15. data/vendor/libgit2/CMakeLists.txt +2 -1
  16. data/vendor/libgit2/include/git2.h +0 -1
  17. data/vendor/libgit2/include/git2/checkout.h +8 -0
  18. data/vendor/libgit2/include/git2/merge.h +8 -0
  19. data/vendor/libgit2/include/git2/push.h +0 -110
  20. data/vendor/libgit2/include/git2/remote.h +30 -1
  21. data/vendor/libgit2/include/git2/revert.h +1 -1
  22. data/vendor/libgit2/include/git2/submodule.h +80 -1
  23. data/vendor/libgit2/include/git2/sys/index.h +2 -2
  24. data/vendor/libgit2/include/git2/{threads.h → sys/openssl.h} +10 -12
  25. data/vendor/libgit2/include/git2/sys/refs.h +11 -0
  26. data/vendor/libgit2/include/git2/tree.h +1 -1
  27. data/vendor/libgit2/include/git2/version.h +3 -3
  28. data/vendor/libgit2/src/attr_file.c +3 -1
  29. data/vendor/libgit2/src/buffer.c +2 -1
  30. data/vendor/libgit2/src/checkout.c +135 -39
  31. data/vendor/libgit2/src/checkout.h +7 -0
  32. data/vendor/libgit2/src/config_file.c +5 -7
  33. data/vendor/libgit2/src/crlf.c +2 -0
  34. data/vendor/libgit2/src/describe.c +6 -2
  35. data/vendor/libgit2/src/diff.c +1 -0
  36. data/vendor/libgit2/src/fileops.c +87 -19
  37. data/vendor/libgit2/src/fileops.h +18 -0
  38. data/vendor/libgit2/src/global.c +1 -1
  39. data/vendor/libgit2/src/ident.c +2 -0
  40. data/vendor/libgit2/src/index.c +4 -4
  41. data/vendor/libgit2/src/merge.c +3 -1
  42. data/vendor/libgit2/src/notes.c +1 -1
  43. data/vendor/libgit2/src/pack.c +1 -0
  44. data/vendor/libgit2/src/path.c +17 -12
  45. data/vendor/libgit2/src/path.h +17 -3
  46. data/vendor/libgit2/src/push.h +110 -0
  47. data/vendor/libgit2/src/rebase.c +4 -2
  48. data/vendor/libgit2/src/remote.c +237 -16
  49. data/vendor/libgit2/src/remote.h +2 -0
  50. data/vendor/libgit2/src/repository.c +7 -3
  51. data/vendor/libgit2/src/submodule.c +229 -18
  52. data/vendor/libgit2/src/transports/local.c +61 -2
  53. data/vendor/libgit2/src/transports/smart_protocol.c +5 -3
  54. data/vendor/libgit2/src/tree.c +2 -2
  55. data/vendor/libgit2/src/util.h +13 -2
  56. data/vendor/libgit2/src/win32/mingw-compat.h +2 -0
  57. data/vendor/libgit2/src/win32/path_w32.h +2 -0
  58. metadata +4 -4
  59. data/vendor/libgit2/cmake/Modules/FindLIBSSH2.cmake +0 -44
@@ -246,7 +246,8 @@ int git_rebase_open(git_rebase **out, git_repository *repo)
246
246
 
247
247
  if (rebase->type == GIT_REBASE_TYPE_NONE) {
248
248
  giterr_set(GITERR_REBASE, "There is no rebase in progress");
249
- return GIT_ENOTFOUND;
249
+ error = GIT_ENOTFOUND;
250
+ goto done;
250
251
  }
251
252
 
252
253
  if ((error = git_buf_puts(&path, rebase->state_path)) < 0)
@@ -591,7 +592,8 @@ static int rebase_init(
591
592
  git_buf state_path = GIT_BUF_INIT;
592
593
  int error;
593
594
 
594
- git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR);
595
+ if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0)
596
+ return error;
595
597
 
596
598
  rebase->repo = repo;
597
599
  rebase->type = GIT_REBASE_TYPE_MERGE;
@@ -18,8 +18,10 @@
18
18
  #include "refs.h"
19
19
  #include "refspec.h"
20
20
  #include "fetchhead.h"
21
+ #include "push.h"
21
22
 
22
23
  static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
24
+ static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name);
23
25
 
24
26
  static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch)
25
27
  {
@@ -137,6 +139,7 @@ static int canonicalize_url(git_buf *out, const char *in)
137
139
  static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
138
140
  {
139
141
  git_remote *remote;
142
+ git_config *config = NULL;
140
143
  git_buf canonical_url = GIT_BUF_INIT, fetchbuf = GIT_BUF_INIT;
141
144
  int error = -1;
142
145
 
@@ -164,6 +167,12 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
164
167
  if (add_refspec(remote, fetch, true) < 0)
165
168
  goto on_error;
166
169
 
170
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
171
+ goto on_error;
172
+
173
+ if (lookup_remote_prune_config(remote, config, name) < 0)
174
+ goto on_error;
175
+
167
176
  /* Move the data over to where the matching functions can find them */
168
177
  if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
169
178
  goto on_error;
@@ -174,12 +183,13 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
174
183
  remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
175
184
 
176
185
  *out = remote;
177
- git_buf_free(&fetchbuf);
178
- git_buf_free(&canonical_url);
179
- return 0;
186
+ error = 0;
180
187
 
181
188
  on_error:
182
- git_remote_free(remote);
189
+ if (error)
190
+ git_remote_free(remote);
191
+
192
+ git_config_free(config);
183
193
  git_buf_free(&fetchbuf);
184
194
  git_buf_free(&canonical_url);
185
195
  return error;
@@ -287,6 +297,7 @@ int git_remote_dup(git_remote **dest, git_remote *source)
287
297
  remote->repo = source->repo;
288
298
  remote->download_tags = source->download_tags;
289
299
  remote->update_fetchhead = source->update_fetchhead;
300
+ remote->prune_refs = source->prune_refs;
290
301
 
291
302
  if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
292
303
  git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
@@ -442,6 +453,9 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
442
453
  if (download_tags_value(remote, config) < 0)
443
454
  goto cleanup;
444
455
 
456
+ if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
457
+ goto cleanup;
458
+
445
459
  /* Move the data over to where the matching functions can find them */
446
460
  if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
447
461
  goto cleanup;
@@ -458,6 +472,30 @@ cleanup:
458
472
  return error;
459
473
  }
460
474
 
475
+ static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name)
476
+ {
477
+ git_buf buf = GIT_BUF_INIT;
478
+ int error = 0;
479
+
480
+ git_buf_printf(&buf, "remote.%s.prune", name);
481
+
482
+ if ((error = git_config_get_bool(&remote->prune_refs, config, git_buf_cstr(&buf))) < 0) {
483
+ if (error == GIT_ENOTFOUND) {
484
+ giterr_clear();
485
+
486
+ if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) {
487
+ if (error == GIT_ENOTFOUND) {
488
+ giterr_clear();
489
+ error = 0;
490
+ }
491
+ }
492
+ }
493
+ }
494
+
495
+ git_buf_free(&buf);
496
+ return error;
497
+ }
498
+
461
499
  static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
462
500
  {
463
501
  git_buf name = GIT_BUF_INIT;
@@ -867,6 +905,11 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs)
867
905
  if (error < 0)
868
906
  return error;
869
907
 
908
+ if (remote->push) {
909
+ git_push_free(remote->push);
910
+ remote->push = NULL;
911
+ }
912
+
870
913
  if ((error = git_fetch_negotiate(remote)) < 0)
871
914
  return error;
872
915
 
@@ -912,6 +955,12 @@ int git_remote_fetch(
912
955
  /* Create "remote/foo" branches for all remote branches */
913
956
  error = git_remote_update_tips(remote, signature, git_buf_cstr(&reflog_msg_buf));
914
957
  git_buf_free(&reflog_msg_buf);
958
+ if (error < 0)
959
+ return error;
960
+
961
+ if (remote->prune_refs)
962
+ error = git_remote_prune(remote);
963
+
915
964
  return error;
916
965
  }
917
966
 
@@ -1066,6 +1115,145 @@ cleanup:
1066
1115
  return error;
1067
1116
  }
1068
1117
 
1118
+ /**
1119
+ * Generate a list of candidates for pruning by getting a list of
1120
+ * references which match the rhs of an active refspec.
1121
+ */
1122
+ static int prune_candidates(git_vector *candidates, git_remote *remote)
1123
+ {
1124
+ git_strarray arr = { 0 };
1125
+ size_t i;
1126
+ int error;
1127
+
1128
+ if ((error = git_reference_list(&arr, remote->repo)) < 0)
1129
+ return error;
1130
+
1131
+ for (i = 0; i < arr.count; i++) {
1132
+ const char *refname = arr.strings[i];
1133
+ char *refname_dup;
1134
+
1135
+ if (!git_remote__matching_dst_refspec(remote, refname))
1136
+ continue;
1137
+
1138
+ refname_dup = git__strdup(refname);
1139
+ GITERR_CHECK_ALLOC(refname_dup);
1140
+
1141
+ if ((error = git_vector_insert(candidates, refname_dup)) < 0)
1142
+ goto out;
1143
+ }
1144
+
1145
+ out:
1146
+ git_strarray_free(&arr);
1147
+ return error;
1148
+ }
1149
+
1150
+ static int find_head(const void *_a, const void *_b)
1151
+ {
1152
+ git_remote_head *a = (git_remote_head *) _a;
1153
+ git_remote_head *b = (git_remote_head *) _b;
1154
+
1155
+ return strcmp(a->name, b->name);
1156
+ }
1157
+
1158
+ int git_remote_prune(git_remote *remote)
1159
+ {
1160
+ size_t i, j;
1161
+ git_vector remote_refs = GIT_VECTOR_INIT;
1162
+ git_vector candidates = GIT_VECTOR_INIT;
1163
+ const git_refspec *spec;
1164
+ const char *refname;
1165
+ int error;
1166
+ git_oid zero_id = {{ 0 }};
1167
+
1168
+ if ((error = ls_to_vector(&remote_refs, remote)) < 0)
1169
+ goto cleanup;
1170
+
1171
+ git_vector_set_cmp(&remote_refs, find_head);
1172
+
1173
+ if ((error = prune_candidates(&candidates, remote)) < 0)
1174
+ goto cleanup;
1175
+
1176
+ /*
1177
+ * Remove those entries from the candidate list for which we
1178
+ * can find a remote reference in at least one refspec.
1179
+ */
1180
+ git_vector_foreach(&candidates, i, refname) {
1181
+ git_vector_foreach(&remote->active_refspecs, j, spec) {
1182
+ git_buf buf = GIT_BUF_INIT;
1183
+ size_t pos;
1184
+ char *src_name;
1185
+ git_remote_head key = {0};
1186
+
1187
+ if (!git_refspec_dst_matches(spec, refname))
1188
+ continue;
1189
+
1190
+ if ((error = git_refspec_rtransform(&buf, spec, refname)) < 0)
1191
+ goto cleanup;
1192
+
1193
+ key.name = (char *) git_buf_cstr(&buf);
1194
+ error = git_vector_search(&pos, &remote_refs, &key);
1195
+ git_buf_free(&buf);
1196
+
1197
+ if (error < 0 && error != GIT_ENOTFOUND)
1198
+ goto cleanup;
1199
+
1200
+ if (error == GIT_ENOTFOUND)
1201
+ continue;
1202
+
1203
+ /* if we did find a source, remove it from the candiates */
1204
+ if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0)
1205
+ goto cleanup;
1206
+
1207
+ git__free(src_name);
1208
+ break;
1209
+ }
1210
+ }
1211
+
1212
+ /*
1213
+ * For those candidates still left in the list, we need to
1214
+ * remove them. We do not remove symrefs, as those are for
1215
+ * stuff like origin/HEAD which will never match, but we do
1216
+ * not want to remove them.
1217
+ */
1218
+ git_vector_foreach(&candidates, i, refname) {
1219
+ git_reference *ref;
1220
+ git_oid id;
1221
+
1222
+ if (refname == NULL)
1223
+ continue;
1224
+
1225
+ error = git_reference_lookup(&ref, remote->repo, refname);
1226
+ /* as we want it gone, let's not consider this an error */
1227
+ if (error == GIT_ENOTFOUND)
1228
+ continue;
1229
+
1230
+ if (error < 0)
1231
+ goto cleanup;
1232
+
1233
+ if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
1234
+ git_reference_free(ref);
1235
+ continue;
1236
+ }
1237
+
1238
+ git_oid_cpy(&id, git_reference_target(ref));
1239
+ error = git_reference_delete(ref);
1240
+ git_reference_free(ref);
1241
+ if (error < 0)
1242
+ goto cleanup;
1243
+
1244
+ if (remote->callbacks.update_tips)
1245
+ error = remote->callbacks.update_tips(refname, &id, &zero_id, remote->callbacks.payload);
1246
+
1247
+ if (error < 0)
1248
+ goto cleanup;
1249
+ }
1250
+
1251
+ cleanup:
1252
+ git_vector_free(&remote_refs);
1253
+ git_vector_free_deep(&candidates);
1254
+ return error;
1255
+ }
1256
+
1069
1257
  static int update_tips_for_spec(
1070
1258
  git_remote *remote,
1071
1259
  git_refspec *spec,
@@ -1275,6 +1463,11 @@ int git_remote_update_tips(
1275
1463
  int error;
1276
1464
  size_t i;
1277
1465
 
1466
+ /* push has its own logic hidden away in the push object */
1467
+ if (remote->push) {
1468
+ return git_push_update_tips(remote->push, signature, reflog_message);
1469
+ }
1470
+
1278
1471
  if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
1279
1472
  return -1;
1280
1473
 
@@ -1355,6 +1548,7 @@ void git_remote_free(git_remote *remote)
1355
1548
  free_refspecs(&remote->passive_refspecs);
1356
1549
  git_vector_free(&remote->passive_refspecs);
1357
1550
 
1551
+ git_push_free(remote->push);
1358
1552
  git__free(remote->url);
1359
1553
  git__free(remote->pushurl);
1360
1554
  git__free(remote->name);
@@ -1465,6 +1659,11 @@ void git_remote_set_autotag(git_remote *remote, git_remote_autotag_option_t valu
1465
1659
  remote->download_tags = value;
1466
1660
  }
1467
1661
 
1662
+ int git_remote_prune_refs(const git_remote *remote)
1663
+ {
1664
+ return remote->prune_refs;
1665
+ }
1666
+
1468
1667
  static int rename_remote_config_section(
1469
1668
  git_repository *repo,
1470
1669
  const char *old_name,
@@ -2117,22 +2316,29 @@ int git_remote_default_branch(git_buf *out, git_remote *remote)
2117
2316
  return git_buf_puts(out, guess->name);
2118
2317
  }
2119
2318
 
2120
- int git_remote_push(git_remote *remote, git_strarray *refspecs, const git_push_options *opts,
2121
- const git_signature *signature, const char *reflog_message)
2319
+ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
2122
2320
  {
2123
- int error;
2124
2321
  size_t i;
2125
- git_push *push = NULL;
2126
- git_remote_callbacks *cbs;
2322
+ int error;
2323
+ git_push *push;
2127
2324
  git_refspec *spec;
2325
+ git_remote_callbacks *cbs;
2128
2326
 
2129
- assert(remote && refspecs);
2327
+ assert(remote);
2130
2328
 
2131
- if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0)
2329
+ if (!git_remote_connected(remote) &&
2330
+ (error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0)
2331
+ goto cleanup;
2332
+
2333
+ if (remote->push) {
2334
+ git_push_free(remote->push);
2335
+ remote->push = NULL;
2336
+ }
2337
+
2338
+ if ((error = git_push_new(&remote->push, remote)) < 0)
2132
2339
  return error;
2133
2340
 
2134
- if ((error = git_push_new(&push, remote)) < 0)
2135
- goto cleanup;
2341
+ push = remote->push;
2136
2342
 
2137
2343
  if (opts && (error = git_push_set_options(push, opts)) < 0)
2138
2344
  goto cleanup;
@@ -2164,10 +2370,25 @@ int git_remote_push(git_remote *remote, git_strarray *refspecs, const git_push_o
2164
2370
  (error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0)
2165
2371
  goto cleanup;
2166
2372
 
2167
- error = git_push_update_tips(push, signature, reflog_message);
2168
-
2169
2373
  cleanup:
2374
+ return error;
2375
+ }
2376
+
2377
+ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts,
2378
+ const git_signature *signature, const char *reflog_message)
2379
+ {
2380
+ int error;
2381
+
2382
+ assert(remote && refspecs);
2383
+
2384
+ if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0)
2385
+ return error;
2386
+
2387
+ if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
2388
+ return error;
2389
+
2390
+ error = git_remote_update_tips(remote, signature, reflog_message);
2391
+
2170
2392
  git_remote_disconnect(remote);
2171
- git_push_free(push);
2172
2393
  return error;
2173
2394
  }
@@ -28,11 +28,13 @@ struct git_remote {
28
28
  void *transport_cb_payload;
29
29
  git_transport *transport;
30
30
  git_repository *repo;
31
+ git_push *push;
31
32
  git_remote_callbacks callbacks;
32
33
  git_transfer_progress stats;
33
34
  unsigned int need_pack;
34
35
  git_remote_autotag_option_t download_tags;
35
36
  int update_fetchhead;
37
+ int prune_refs;
36
38
  int passed_refspecs;
37
39
  };
38
40
 
@@ -656,7 +656,8 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
656
656
  git_buf odb_path = GIT_BUF_INIT;
657
657
  git_odb *odb;
658
658
 
659
- git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR);
659
+ if ((error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR)) < 0)
660
+ return error;
660
661
 
661
662
  error = git_odb_open(&odb, odb_path.ptr);
662
663
  if (!error) {
@@ -741,7 +742,8 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
741
742
  git_buf index_path = GIT_BUF_INIT;
742
743
  git_index *index;
743
744
 
744
- git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE);
745
+ if ((error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE)) < 0)
746
+ return error;
745
747
 
746
748
  error = git_index_open(&index, index_path.ptr);
747
749
  if (!error) {
@@ -2068,7 +2070,9 @@ int git_repository_is_shallow(git_repository *repo)
2068
2070
  struct stat st;
2069
2071
  int error;
2070
2072
 
2071
- git_buf_joinpath(&path, repo->path_repository, "shallow");
2073
+ if ((error = git_buf_joinpath(&path, repo->path_repository, "shallow")) < 0)
2074
+ return error;
2075
+
2072
2076
  error = git_path_lstat(path.ptr, &st);
2073
2077
  git_buf_free(&path);
2074
2078
 
@@ -381,10 +381,6 @@ int git_submodule_add_setup(
381
381
  return GIT_EEXISTS;
382
382
  }
383
383
 
384
- /* resolve parameters */
385
- if ((error = git_submodule_resolve_url(&real_url, repo, url)) < 0)
386
- goto cleanup;
387
-
388
384
  /* validate and normalize path */
389
385
 
390
386
  if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
@@ -409,7 +405,7 @@ int git_submodule_add_setup(
409
405
  goto cleanup;
410
406
 
411
407
  if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
412
- (error = git_config_file_set_string(mods, name.ptr, real_url.ptr)) < 0)
408
+ (error = git_config_file_set_string(mods, name.ptr, url)) < 0)
413
409
  goto cleanup;
414
410
 
415
411
  git_buf_clear(&name);
@@ -425,7 +421,12 @@ int git_submodule_add_setup(
425
421
  */
426
422
  if (!(git_path_exists(name.ptr) &&
427
423
  git_path_contains(&name, DOT_GIT))) {
428
- if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
424
+
425
+ /* resolve the actual URL to use */
426
+ if ((error = git_submodule_resolve_url(&real_url, repo, url)) < 0)
427
+ goto cleanup;
428
+
429
+ if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
429
430
  goto cleanup;
430
431
  }
431
432
 
@@ -466,13 +467,24 @@ int git_submodule_repo_init(
466
467
  {
467
468
  int error;
468
469
  git_repository *sub_repo = NULL;
470
+ const char *configured_url;
471
+ git_config *cfg = NULL;
472
+ git_buf buf = GIT_BUF_INIT;
469
473
 
470
474
  assert(out && sm);
471
475
 
472
- error = submodule_repo_init(&sub_repo, sm->repo, sm->path, sm->url, use_gitlink);
476
+ /* get the configured remote url of the submodule */
477
+ if ((error = git_buf_printf(&buf, "submodule.%s.url", sm->name)) < 0 ||
478
+ (error = git_repository_config_snapshot(&cfg, sm->repo)) < 0 ||
479
+ (error = git_config_get_string(&configured_url, cfg, buf.ptr)) < 0 ||
480
+ (error = submodule_repo_init(&sub_repo, sm->repo, sm->path, configured_url, use_gitlink)) < 0)
481
+ goto done;
473
482
 
474
483
  *out = sub_repo;
475
484
 
485
+ done:
486
+ git_config_free(cfg);
487
+ git_buf_free(&buf);
476
488
  return error;
477
489
  }
478
490
 
@@ -778,7 +790,7 @@ git_submodule_ignore_t git_submodule_set_ignore(
778
790
  return old;
779
791
  }
780
792
 
781
- git_submodule_update_t git_submodule_update(git_submodule *submodule)
793
+ git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
782
794
  {
783
795
  assert(submodule);
784
796
  return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
@@ -823,11 +835,193 @@ git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules(
823
835
  return old;
824
836
  }
825
837
 
838
+ static int submodule_repo_create(
839
+ git_repository **out,
840
+ git_repository *parent_repo,
841
+ const char *path)
842
+ {
843
+ int error = 0;
844
+ git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
845
+ git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
846
+ git_repository *subrepo = NULL;
847
+
848
+ initopt.flags =
849
+ GIT_REPOSITORY_INIT_MKPATH |
850
+ GIT_REPOSITORY_INIT_NO_REINIT |
851
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
852
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
853
+
854
+ /* Workdir: path to sub-repo working directory */
855
+ error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
856
+ if (error < 0)
857
+ goto cleanup;
858
+
859
+ initopt.workdir_path = workdir.ptr;
860
+
861
+ /**
862
+ * Repodir: path to the sub-repo. sub-repo goes in:
863
+ * <repo-dir>/modules/<name>/ with a gitlink in the
864
+ * sub-repo workdir directory to that repository.
865
+ */
866
+ error = git_buf_join3(
867
+ &repodir, '/', git_repository_path(parent_repo), "modules", path);
868
+ if (error < 0)
869
+ goto cleanup;
870
+
871
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
872
+
873
+ cleanup:
874
+ git_buf_free(&workdir);
875
+ git_buf_free(&repodir);
876
+
877
+ *out = subrepo;
878
+
879
+ return error;
880
+ }
881
+
882
+ /**
883
+ * Callback to override sub-repository creation when
884
+ * cloning a sub-repository.
885
+ */
886
+ static int git_submodule_update_repo_init_cb(
887
+ git_repository **out,
888
+ const char *path,
889
+ int bare,
890
+ void *payload)
891
+ {
892
+ git_submodule *sm;
893
+
894
+ GIT_UNUSED(bare);
895
+
896
+ sm = payload;
897
+
898
+ return submodule_repo_create(out, sm->repo, path);
899
+ }
900
+
901
+ int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version)
902
+ {
903
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
904
+ opts, version, git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_INIT);
905
+ return 0;
906
+ }
907
+
908
+ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options)
909
+ {
910
+ int error;
911
+ unsigned int submodule_status;
912
+ git_config *config = NULL;
913
+ const char *submodule_url;
914
+ git_repository *sub_repo = NULL;
915
+ git_remote *remote = NULL;
916
+ git_object *target_commit = NULL;
917
+ git_buf buf = GIT_BUF_INIT;
918
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
919
+ git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT;
920
+
921
+ assert(sm);
922
+
923
+ if (_update_options)
924
+ memcpy(&update_options, _update_options, sizeof(git_submodule_update_options));
925
+
926
+ GITERR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
927
+
928
+ /* Copy over the remote callbacks */
929
+ clone_options.remote_callbacks = update_options.remote_callbacks;
930
+ clone_options.signature = update_options.signature;
931
+
932
+ /* Get the status of the submodule to determine if it is already initialized */
933
+ if ((error = git_submodule_status(&submodule_status, sm)) < 0)
934
+ goto done;
935
+
936
+ /*
937
+ * If submodule work dir is not already initialized, check to see
938
+ * what we need to do (initialize, clone, return error...)
939
+ */
940
+ if (submodule_status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) {
941
+ /*
942
+ * Work dir is not initialized, check to see if the submodule
943
+ * info has been copied into .git/config
944
+ */
945
+ if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
946
+ (error = git_buf_printf(&buf, "submodule.%s.url", git_submodule_name(sm))) < 0)
947
+ goto done;
948
+
949
+ if ((error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) {
950
+ /*
951
+ * If the error is not "not found" or if it is "not found" and we are not
952
+ * initializing the submodule, then return error.
953
+ */
954
+ if (error != GIT_ENOTFOUND)
955
+ goto done;
956
+
957
+ if (error == GIT_ENOTFOUND && !init) {
958
+ giterr_set(GITERR_SUBMODULE, "Submodule is not initialized.");
959
+ error = GIT_ERROR;
960
+ goto done;
961
+ }
962
+
963
+ /* The submodule has not been initialized yet - initialize it now.*/
964
+ if ((error = git_submodule_init(sm, 0)) < 0)
965
+ goto done;
966
+
967
+ git_config_free(config);
968
+ config = NULL;
969
+
970
+ if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
971
+ (error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0)
972
+ goto done;
973
+ }
974
+
975
+ /** submodule is initialized - now clone it **/
976
+ /* override repo creation */
977
+ clone_options.repository_cb = git_submodule_update_repo_init_cb;
978
+ clone_options.repository_cb_payload = sm;
979
+
980
+ /*
981
+ * Do not perform checkout as part of clone, instead we
982
+ * will checkout the specific commit manually.
983
+ */
984
+ clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
985
+ update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy;
986
+
987
+ if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
988
+ (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm), update_options.signature, NULL)) < 0 ||
989
+ (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
990
+ goto done;
991
+ } else {
992
+ /**
993
+ * Work dir is initialized - look up the commit in the parent repository's index,
994
+ * update the workdir contents of the subrepository, and set the subrepository's
995
+ * head to the new commit.
996
+ */
997
+ if ((error = git_submodule_open(&sub_repo, sm)) < 0 ||
998
+ (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0 ||
999
+ (error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 ||
1000
+ (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm), update_options.signature, NULL)) < 0)
1001
+ goto done;
1002
+
1003
+ /* Invalidate the wd flags as the workdir has been updated. */
1004
+ sm->flags = sm->flags &
1005
+ ~(GIT_SUBMODULE_STATUS_IN_WD |
1006
+ GIT_SUBMODULE_STATUS__WD_OID_VALID |
1007
+ GIT_SUBMODULE_STATUS__WD_SCANNED);
1008
+ }
1009
+
1010
+ done:
1011
+ git_buf_free(&buf);
1012
+ git_config_free(config);
1013
+ git_object_free(target_commit);
1014
+ git_remote_free(remote);
1015
+ git_repository_free(sub_repo);
1016
+
1017
+ return error;
1018
+ }
1019
+
826
1020
  int git_submodule_init(git_submodule *sm, int overwrite)
827
1021
  {
828
1022
  int error;
829
1023
  const char *val;
830
- git_buf key = GIT_BUF_INIT;
1024
+ git_buf key = GIT_BUF_INIT, effective_submodule_url = GIT_BUF_INIT;
831
1025
  git_config *cfg = NULL;
832
1026
 
833
1027
  if (!sm->url) {
@@ -841,9 +1035,10 @@ int git_submodule_init(git_submodule *sm, int overwrite)
841
1035
 
842
1036
  /* write "submodule.NAME.url" */
843
1037
 
844
- if ((error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
1038
+ if ((error = git_submodule_resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 ||
1039
+ (error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
845
1040
  (error = git_config__update_entry(
846
- cfg, key.ptr, sm->url, overwrite != 0, false)) < 0)
1041
+ cfg, key.ptr, effective_submodule_url.ptr, overwrite != 0, false)) < 0)
847
1042
  goto cleanup;
848
1043
 
849
1044
  /* write "submodule.NAME.update" if not default */
@@ -861,6 +1056,7 @@ int git_submodule_init(git_submodule *sm, int overwrite)
861
1056
  cleanup:
862
1057
  git_config_free(cfg);
863
1058
  git_buf_free(&key);
1059
+ git_buf_free(&effective_submodule_url);
864
1060
 
865
1061
  return error;
866
1062
  }
@@ -1853,16 +2049,31 @@ static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo)
1853
2049
  if ((error = git_repository_head(&head, repo)) < 0)
1854
2050
  return error;
1855
2051
 
2052
+ /**
2053
+ * If head does not refer to a branch, then return
2054
+ * GIT_ENOTFOUND to indicate that we could not find
2055
+ * a remote key for the local tracking branch HEAD points to.
2056
+ **/
2057
+ if (!git_reference_is_branch(head)) {
2058
+ giterr_set(GITERR_INVALID,
2059
+ "HEAD does not refer to a branch.");
2060
+ error = GIT_ENOTFOUND;
2061
+ goto done;
2062
+ }
2063
+
1856
2064
  /* lookup remote tracking branch of HEAD */
1857
- if (!(error = git_branch_upstream_name(
1858
- &upstream_name, repo, git_reference_name(head))))
1859
- {
1860
- /* lookup remote of remote tracking branch */
1861
- error = git_branch_remote_name(remote_name, repo, upstream_name.ptr);
2065
+ if ((error = git_branch_upstream_name(
2066
+ &upstream_name,
2067
+ repo,
2068
+ git_reference_name(head))) < 0)
2069
+ goto done;
1862
2070
 
1863
- git_buf_free(&upstream_name);
1864
- }
2071
+ /* lookup remote of remote tracking branch */
2072
+ if ((error = git_branch_remote_name(remote_name, repo, upstream_name.ptr)) < 0)
2073
+ goto done;
1865
2074
 
2075
+ done:
2076
+ git_buf_free(&upstream_name);
1866
2077
  git_reference_free(head);
1867
2078
 
1868
2079
  return error;