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
@@ -14,4 +14,8 @@
14
14
  int git_protocol_store_refs(git_transport *t, int flushes);
15
15
  int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps);
16
16
 
17
+ #define GIT_SIDE_BAND_DATA 1
18
+ #define GIT_SIDE_BAND_PROGRESS 2
19
+ #define GIT_SIDE_BAND_ERROR 3
20
+
17
21
  #endif
@@ -372,7 +372,7 @@ int git_reflog_rename(git_reference *ref, const char *new_name)
372
372
  goto cleanup;
373
373
 
374
374
  if (git_path_isdir(git_buf_cstr(&new_path)) &&
375
- (git_futils_rmdir_r(git_buf_cstr(&new_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0))
375
+ (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0))
376
376
  goto cleanup;
377
377
 
378
378
  if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0)
@@ -15,6 +15,7 @@
15
15
  #include <git2/tag.h>
16
16
  #include <git2/object.h>
17
17
  #include <git2/oid.h>
18
+ #include <git2/branch.h>
18
19
 
19
20
  GIT__USE_STRMAP;
20
21
 
@@ -68,11 +69,6 @@ static int reference_path_available(git_repository *repo,
68
69
  static int reference_delete(git_reference *ref);
69
70
  static int reference_lookup(git_reference *ref);
70
71
 
71
- /* name normalization */
72
- static int normalize_name(char *buffer_out, size_t out_size,
73
- const char *name, int is_oid_ref);
74
-
75
-
76
72
  void git_reference_free(git_reference *reference)
77
73
  {
78
74
  if (reference == NULL)
@@ -159,11 +155,26 @@ static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
159
155
 
160
156
  static int loose_parse_oid(git_oid *oid, git_buf *file_content)
161
157
  {
162
- /* File format: 40 chars (OID) */
163
- if (git_buf_len(file_content) == GIT_OID_HEXSZ &&
164
- git_oid_fromstr(oid, git_buf_cstr(file_content)) == 0)
158
+ size_t len;
159
+ const char *str;
160
+
161
+ len = git_buf_len(file_content);
162
+ if (len < GIT_OID_HEXSZ)
163
+ goto corrupted;
164
+
165
+ /* str is guranteed to be zero-terminated */
166
+ str = git_buf_cstr(file_content);
167
+
168
+ /* If the file is longer than 40 chars, the 41st must be a space */
169
+ if (git_oid_fromstr(oid, git_buf_cstr(file_content)) < 0)
170
+ goto corrupted;
171
+
172
+ /* If the file is longer than 40 chars, the 41st must be a space */
173
+ str += GIT_OID_HEXSZ;
174
+ if (*str == '\0' || git__isspace(*str))
165
175
  return 0;
166
176
 
177
+ corrupted:
167
178
  giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
168
179
  return -1;
169
180
  }
@@ -200,8 +211,6 @@ static int loose_lookup(git_reference *ref)
200
211
  if (!updated)
201
212
  return 0;
202
213
 
203
- git_buf_rtrim(&ref_file);
204
-
205
214
  if (ref->flags & GIT_REF_SYMBOLIC) {
206
215
  git__free(ref->target.symbolic);
207
216
  ref->target.symbolic = NULL;
@@ -211,6 +220,7 @@ static int loose_lookup(git_reference *ref)
211
220
 
212
221
  if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
213
222
  ref->flags |= GIT_REF_SYMBOLIC;
223
+ git_buf_rtrim(&ref_file);
214
224
  result = loose_parse_symbolic(ref, &ref_file);
215
225
  } else {
216
226
  ref->flags |= GIT_REF_OID;
@@ -266,14 +276,15 @@ static int loose_write(git_reference *ref)
266
276
  if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
267
277
  return -1;
268
278
 
269
- /* Remove a possibly existing empty directory hierarchy
279
+ /* Remove a possibly existing empty directory hierarchy
270
280
  * which name would collide with the reference name
271
281
  */
272
- if (git_path_isdir(git_buf_cstr(&ref_path)) &&
273
- (git_futils_rmdir_r(git_buf_cstr(&ref_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) {
274
- git_buf_free(&ref_path);
275
- return -1;
276
- }
282
+ if (git_path_isdir(git_buf_cstr(&ref_path)) &&
283
+ git_futils_rmdir_r(git_buf_cstr(&ref_path), NULL,
284
+ GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0) {
285
+ git_buf_free(&ref_path);
286
+ return -1;
287
+ }
277
288
 
278
289
  if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
279
290
  git_buf_free(&ref_path);
@@ -499,6 +510,10 @@ static int _dirent_loose_listall(void *_data, git_buf *full_path)
499
510
  return 0; /* we are filtering out this reference */
500
511
  }
501
512
 
513
+ /* Locked references aren't returned */
514
+ if (!git__suffixcmp(file_path, GIT_FILELOCK_EXTENSION))
515
+ return 0;
516
+
502
517
  if (data->callback(file_path, data->callback_payload))
503
518
  data->callback_error = GIT_EUSER;
504
519
 
@@ -1099,9 +1114,12 @@ int git_reference_lookup_resolved(
1099
1114
  scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char));
1100
1115
  GITERR_CHECK_ALLOC(scan->name);
1101
1116
 
1102
- if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) {
1103
- git_reference_free(scan);
1104
- return result;
1117
+ if ((result = git_reference__normalize_name_lax(
1118
+ scan->name,
1119
+ GIT_REFNAME_MAX,
1120
+ name)) < 0) {
1121
+ git_reference_free(scan);
1122
+ return result;
1105
1123
  }
1106
1124
 
1107
1125
  scan->target.symbolic = git__strdup(scan->name);
@@ -1197,12 +1215,16 @@ int git_reference_create_symbolic(
1197
1215
  {
1198
1216
  char normalized[GIT_REFNAME_MAX];
1199
1217
  git_reference *ref = NULL;
1218
+ int error;
1200
1219
 
1201
- if (normalize_name(normalized, sizeof(normalized), name, 0) < 0)
1202
- return -1;
1220
+ if (git_reference__normalize_name_lax(
1221
+ normalized,
1222
+ sizeof(normalized),
1223
+ name) < 0)
1224
+ return -1;
1203
1225
 
1204
- if (reference_can_write(repo, normalized, NULL, force) < 0)
1205
- return -1;
1226
+ if ((error = reference_can_write(repo, normalized, NULL, force)) < 0)
1227
+ return error;
1206
1228
 
1207
1229
  if (reference_alloc(&ref, repo, normalized) < 0)
1208
1230
  return -1;
@@ -1231,14 +1253,18 @@ int git_reference_create_oid(
1231
1253
  const git_oid *id,
1232
1254
  int force)
1233
1255
  {
1256
+ int error;
1234
1257
  git_reference *ref = NULL;
1235
1258
  char normalized[GIT_REFNAME_MAX];
1236
1259
 
1237
- if (normalize_name(normalized, sizeof(normalized), name, 1) < 0)
1238
- return -1;
1260
+ if (git_reference__normalize_name_lax(
1261
+ normalized,
1262
+ sizeof(normalized),
1263
+ name) < 0)
1264
+ return -1;
1239
1265
 
1240
- if (reference_can_write(repo, normalized, NULL, force) < 0)
1241
- return -1;
1266
+ if ((error = reference_can_write(repo, normalized, NULL, force)) < 0)
1267
+ return error;
1242
1268
 
1243
1269
  if (reference_alloc(&ref, repo, name) < 0)
1244
1270
  return -1;
@@ -1314,8 +1340,11 @@ int git_reference_set_target(git_reference *ref, const char *target)
1314
1340
  return -1;
1315
1341
  }
1316
1342
 
1317
- if (normalize_name(normalized, sizeof(normalized), target, 0))
1318
- return -1;
1343
+ if (git_reference__normalize_name_lax(
1344
+ normalized,
1345
+ sizeof(normalized),
1346
+ target))
1347
+ return -1;
1319
1348
 
1320
1349
  git__free(ref->target.symbolic);
1321
1350
  ref->target.symbolic = git__strdup(normalized);
@@ -1327,24 +1356,36 @@ int git_reference_set_target(git_reference *ref, const char *target)
1327
1356
  int git_reference_rename(git_reference *ref, const char *new_name, int force)
1328
1357
  {
1329
1358
  int result;
1359
+ unsigned int normalization_flags;
1330
1360
  git_buf aux_path = GIT_BUF_INIT;
1331
1361
  char normalized[GIT_REFNAME_MAX];
1362
+ bool should_head_be_updated = false;
1332
1363
 
1333
- const char *head_target = NULL;
1334
- git_reference *head = NULL;
1364
+ normalization_flags = ref->flags & GIT_REF_SYMBOLIC ?
1365
+ GIT_REF_FORMAT_ALLOW_ONELEVEL
1366
+ : GIT_REF_FORMAT_NORMAL;
1335
1367
 
1336
- if (normalize_name(normalized, sizeof(normalized),
1337
- new_name, ref->flags & GIT_REF_OID) < 0)
1338
- return -1;
1368
+ if (git_reference_normalize_name(
1369
+ normalized,
1370
+ sizeof(normalized),
1371
+ new_name,
1372
+ normalization_flags) < 0)
1373
+ return -1;
1339
1374
 
1340
- if (reference_can_write(ref->owner, normalized, ref->name, force) < 0)
1341
- return -1;
1375
+ if ((result = reference_can_write(ref->owner, normalized, ref->name, force)) < 0)
1376
+ return result;
1342
1377
 
1343
1378
  /* Initialize path now so we won't get an allocation failure once
1344
1379
  * we actually start removing things. */
1345
1380
  if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0)
1346
1381
  return -1;
1347
1382
 
1383
+ /*
1384
+ * Check if we have to update HEAD.
1385
+ */
1386
+ if ((should_head_be_updated = git_branch_is_head(ref)) < 0)
1387
+ goto cleanup;
1388
+
1348
1389
  /*
1349
1390
  * Now delete the old ref and remove an possibly existing directory
1350
1391
  * named `new_name`. Note that using the internal `reference_delete`
@@ -1369,25 +1410,13 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
1369
1410
  goto rollback;
1370
1411
 
1371
1412
  /*
1372
- * Check if we have to update HEAD.
1413
+ * Update HEAD it was poiting to the reference being renamed.
1373
1414
  */
1374
- if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE) < 0) {
1375
- giterr_set(GITERR_REFERENCE,
1376
- "Failed to update HEAD after renaming reference");
1377
- goto cleanup;
1378
- }
1379
-
1380
- head_target = git_reference_target(head);
1381
-
1382
- if (head_target && !strcmp(head_target, ref->name)) {
1383
- git_reference_free(head);
1384
- head = NULL;
1385
-
1386
- if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1) < 0) {
1415
+ if (should_head_be_updated &&
1416
+ git_repository_set_head(ref->owner, new_name) < 0) {
1387
1417
  giterr_set(GITERR_REFERENCE,
1388
1418
  "Failed to update HEAD after renaming reference");
1389
1419
  goto cleanup;
1390
- }
1391
1420
  }
1392
1421
 
1393
1422
  /*
@@ -1405,12 +1434,10 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
1405
1434
  /* The reference is no longer packed */
1406
1435
  ref->flags &= ~GIT_REF_PACKED;
1407
1436
 
1408
- git_reference_free(head);
1409
1437
  git_buf_free(&aux_path);
1410
1438
  return 0;
1411
1439
 
1412
1440
  cleanup:
1413
- git_reference_free(head);
1414
1441
  git_buf_free(&aux_path);
1415
1442
  return -1;
1416
1443
 
@@ -1565,112 +1592,185 @@ static int is_valid_ref_char(char ch)
1565
1592
  }
1566
1593
  }
1567
1594
 
1568
- static int normalize_name(
1569
- char *buffer_out,
1570
- size_t out_size,
1571
- const char *name,
1572
- int is_oid_ref)
1595
+ static int ensure_segment_validity(const char *name)
1573
1596
  {
1574
- const char *name_end, *buffer_out_start;
1575
- const char *current;
1576
- int contains_a_slash = 0;
1597
+ const char *current = name;
1598
+ char prev = '\0';
1577
1599
 
1578
- assert(name && buffer_out);
1600
+ if (*current == '.')
1601
+ return -1; /* Refname starts with "." */
1579
1602
 
1580
- buffer_out_start = buffer_out;
1581
- current = name;
1582
- name_end = name + strlen(name);
1603
+ for (current = name; ; current++) {
1604
+ if (*current == '\0' || *current == '/')
1605
+ break;
1583
1606
 
1584
- /* Terminating null byte */
1585
- out_size--;
1607
+ if (!is_valid_ref_char(*current))
1608
+ return -1; /* Illegal character in refname */
1586
1609
 
1587
- /* A refname can not be empty */
1588
- if (name_end == name)
1589
- goto invalid_name;
1610
+ if (prev == '.' && *current == '.')
1611
+ return -1; /* Refname contains ".." */
1590
1612
 
1591
- /* A refname can not end with a dot or a slash */
1592
- if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
1593
- goto invalid_name;
1613
+ if (prev == '@' && *current == '{')
1614
+ return -1; /* Refname contains "@{" */
1594
1615
 
1595
- while (current < name_end && out_size) {
1596
- if (!is_valid_ref_char(*current))
1597
- goto invalid_name;
1616
+ prev = *current;
1617
+ }
1618
+
1619
+ return (int)(current - name);
1620
+ }
1598
1621
 
1599
- if (buffer_out > buffer_out_start) {
1600
- char prev = *(buffer_out - 1);
1622
+ static bool is_all_caps_and_underscore(const char *name, size_t len)
1623
+ {
1624
+ size_t i;
1625
+ char c;
1601
1626
 
1602
- /* A refname can not start with a dot nor contain a double dot */
1603
- if (*current == '.' && ((prev == '.') || (prev == '/')))
1604
- goto invalid_name;
1627
+ assert(name && len > 0);
1605
1628
 
1606
- /* '@{' is forbidden within a refname */
1607
- if (*current == '{' && prev == '@')
1608
- goto invalid_name;
1629
+ for (i = 0; i < len; i++)
1630
+ {
1631
+ c = name[i];
1632
+ if ((c < 'A' || c > 'Z') && c != '_')
1633
+ return false;
1634
+ }
1609
1635
 
1610
- /* Prevent multiple slashes from being added to the output */
1611
- if (*current == '/' && prev == '/') {
1612
- current++;
1613
- continue;
1636
+ if (*name == '_' || name[len - 1] == '_')
1637
+ return false;
1638
+
1639
+ return true;
1640
+ }
1641
+
1642
+ int git_reference__normalize_name(
1643
+ git_buf *buf,
1644
+ const char *name,
1645
+ unsigned int flags)
1646
+ {
1647
+ // Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
1648
+
1649
+ char *current;
1650
+ int segment_len, segments_count = 0, error = -1;
1651
+ unsigned int process_flags;
1652
+ bool normalize = (buf != NULL);
1653
+ assert(name);
1654
+
1655
+ process_flags = flags;
1656
+ current = (char *)name;
1657
+
1658
+ if (normalize)
1659
+ git_buf_clear(buf);
1660
+
1661
+ while (true) {
1662
+ segment_len = ensure_segment_validity(current);
1663
+ if (segment_len < 0) {
1664
+ if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) &&
1665
+ current[0] == '*' &&
1666
+ (current[1] == '\0' || current[1] == '/')) {
1667
+ /* Accept one wildcard as a full refname component. */
1668
+ process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN;
1669
+ segment_len = 1;
1670
+ } else
1671
+ goto cleanup;
1672
+ }
1673
+
1674
+ if (segment_len > 0) {
1675
+ if (normalize) {
1676
+ size_t cur_len = git_buf_len(buf);
1677
+
1678
+ git_buf_joinpath(buf, git_buf_cstr(buf), current);
1679
+ git_buf_truncate(buf,
1680
+ cur_len + segment_len + (segments_count ? 1 : 0));
1681
+
1682
+ if (git_buf_oom(buf))
1683
+ goto cleanup;
1614
1684
  }
1685
+
1686
+ segments_count++;
1615
1687
  }
1616
1688
 
1617
- if (*current == '/')
1618
- contains_a_slash = 1;
1689
+ if (current[segment_len] == '\0')
1690
+ break;
1619
1691
 
1620
- *buffer_out++ = *current++;
1621
- out_size--;
1692
+ current += segment_len + 1;
1622
1693
  }
1623
1694
 
1624
- if (!out_size)
1625
- goto invalid_name;
1695
+ /* A refname can not be empty */
1696
+ if (segment_len == 0 && segments_count == 0)
1697
+ goto cleanup;
1626
1698
 
1627
- /* Object id refname have to contain at least one slash, except
1628
- * for HEAD in a detached state or MERGE_HEAD if we're in the
1629
- * middle of a merge */
1630
- if (is_oid_ref &&
1631
- !contains_a_slash &&
1632
- strcmp(name, GIT_HEAD_FILE) != 0 &&
1633
- strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&
1634
- strcmp(name, GIT_FETCH_HEAD_FILE) != 0)
1635
- goto invalid_name;
1699
+ /* A refname can not end with "." */
1700
+ if (current[segment_len - 1] == '.')
1701
+ goto cleanup;
1702
+
1703
+ /* A refname can not end with "/" */
1704
+ if (current[segment_len - 1] == '/')
1705
+ goto cleanup;
1636
1706
 
1637
1707
  /* A refname can not end with ".lock" */
1638
1708
  if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
1639
- goto invalid_name;
1709
+ goto cleanup;
1640
1710
 
1641
- *buffer_out = '\0';
1711
+ if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL))
1712
+ goto cleanup;
1642
1713
 
1643
- /*
1644
- * For object id references, name has to start with refs/. Again,
1645
- * we need to allow HEAD to be in a detached state.
1646
- */
1647
- if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
1648
- strcmp(buffer_out_start, GIT_HEAD_FILE)))
1649
- goto invalid_name;
1714
+ if ((segments_count == 1 ) &&
1715
+ !(is_all_caps_and_underscore(name, (size_t)segment_len) ||
1716
+ ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
1717
+ goto cleanup;
1650
1718
 
1651
- return 0;
1719
+ if ((segments_count > 1)
1720
+ && (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
1721
+ goto cleanup;
1652
1722
 
1653
- invalid_name:
1654
- giterr_set(GITERR_REFERENCE, "The given reference name is not valid");
1655
- return -1;
1723
+ error = 0;
1724
+
1725
+ cleanup:
1726
+ if (error)
1727
+ giterr_set(
1728
+ GITERR_REFERENCE,
1729
+ "The given reference name '%s' is not valid", name);
1730
+
1731
+ return error;
1656
1732
  }
1657
1733
 
1658
- int git_reference__normalize_name(
1734
+ int git_reference_normalize_name(
1659
1735
  char *buffer_out,
1660
- size_t out_size,
1661
- const char *name)
1736
+ size_t buffer_size,
1737
+ const char *name,
1738
+ unsigned int flags)
1662
1739
  {
1663
- return normalize_name(buffer_out, out_size, name, 0);
1740
+ git_buf buf = GIT_BUF_INIT;
1741
+ int error;
1742
+
1743
+ if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
1744
+ goto cleanup;
1745
+
1746
+ if (git_buf_len(&buf) > buffer_size - 1) {
1747
+ giterr_set(
1748
+ GITERR_REFERENCE,
1749
+ "The provided buffer is too short to hold the normalization of '%s'", name);
1750
+ error = GIT_EBUFS;
1751
+ goto cleanup;
1752
+ }
1753
+
1754
+ git_buf_copy_cstr(buffer_out, buffer_size, &buf);
1755
+
1756
+ error = 0;
1757
+
1758
+ cleanup:
1759
+ git_buf_free(&buf);
1760
+ return error;
1664
1761
  }
1665
1762
 
1666
- int git_reference__normalize_name_oid(
1763
+ int git_reference__normalize_name_lax(
1667
1764
  char *buffer_out,
1668
1765
  size_t out_size,
1669
1766
  const char *name)
1670
1767
  {
1671
- return normalize_name(buffer_out, out_size, name, 1);
1768
+ return git_reference_normalize_name(
1769
+ buffer_out,
1770
+ out_size,
1771
+ name,
1772
+ GIT_REF_FORMAT_ALLOW_ONELEVEL);
1672
1773
  }
1673
-
1674
1774
  #define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
1675
1775
 
1676
1776
  int git_reference_cmp(git_reference *ref1, git_reference *ref2)
@@ -1804,6 +1904,74 @@ int git_reference_has_log(
1804
1904
  int git_reference_is_branch(git_reference *ref)
1805
1905
  {
1806
1906
  assert(ref);
1807
-
1808
1907
  return git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0;
1809
1908
  }
1909
+
1910
+ int git_reference_is_remote(git_reference *ref)
1911
+ {
1912
+ assert(ref);
1913
+ return git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0;
1914
+ }
1915
+
1916
+ static int peel_error(int error, git_reference *ref, const char* msg)
1917
+ {
1918
+ giterr_set(
1919
+ GITERR_INVALID,
1920
+ "The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
1921
+ return error;
1922
+ }
1923
+
1924
+ static int reference_target(git_object **object, git_reference *ref)
1925
+ {
1926
+ const git_oid *oid;
1927
+
1928
+ oid = git_reference_oid(ref);
1929
+
1930
+ return git_object_lookup(object, git_reference_owner(ref), oid, GIT_OBJ_ANY);
1931
+ }
1932
+
1933
+ int git_reference_peel(
1934
+ git_object **peeled,
1935
+ git_reference *ref,
1936
+ git_otype target_type)
1937
+ {
1938
+ git_reference *resolved = NULL;
1939
+ git_object *target = NULL;
1940
+ int error;
1941
+
1942
+ assert(ref);
1943
+
1944
+ if ((error = git_reference_resolve(&resolved, ref)) < 0)
1945
+ return peel_error(error, ref, "Cannot resolve reference");
1946
+
1947
+ if ((error = reference_target(&target, resolved)) < 0) {
1948
+ peel_error(error, ref, "Cannot retrieve reference target");
1949
+ goto cleanup;
1950
+ }
1951
+
1952
+ if (target_type == GIT_OBJ_ANY && git_object_type(target) != GIT_OBJ_TAG)
1953
+ error = git_object__dup(peeled, target);
1954
+ else
1955
+ error = git_object_peel(peeled, target, target_type);
1956
+
1957
+ cleanup:
1958
+ git_object_free(target);
1959
+ git_reference_free(resolved);
1960
+ return error;
1961
+ }
1962
+
1963
+ int git_reference__is_valid_name(
1964
+ const char *refname,
1965
+ unsigned int flags)
1966
+ {
1967
+ giterr_clear();
1968
+ return git_reference__normalize_name(NULL, refname, flags) == 0;
1969
+ }
1970
+
1971
+ int git_reference_is_valid_name(
1972
+ const char *refname)
1973
+ {
1974
+ return git_reference__is_valid_name(
1975
+ refname,
1976
+ GIT_REF_FORMAT_ALLOW_ONELEVEL);
1977
+ }