rugged 0.22.0b5 → 0.22.1b1

Sign up to get free protection for your applications and to get access to all the features.
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;