rugged 0.21.4 → 0.22.0b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (224) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -5
  3. data/ext/rugged/extconf.rb +9 -9
  4. data/ext/rugged/rugged.c +4 -2
  5. data/ext/rugged/rugged.h +3 -7
  6. data/ext/rugged/rugged_blob.c +57 -0
  7. data/ext/rugged/rugged_cred.c +23 -0
  8. data/ext/rugged/rugged_index.c +6 -2
  9. data/ext/rugged/rugged_remote.c +65 -52
  10. data/ext/rugged/rugged_remote_collection.c +59 -10
  11. data/ext/rugged/rugged_repo.c +345 -11
  12. data/ext/rugged/rugged_revwalk.c +10 -0
  13. data/ext/rugged/rugged_submodule.c +1042 -0
  14. data/ext/rugged/rugged_submodule_collection.c +236 -0
  15. data/ext/rugged/rugged_tag_collection.c +70 -2
  16. data/ext/rugged/rugged_tree.c +29 -10
  17. data/lib/rugged.rb +3 -0
  18. data/lib/rugged/attributes.rb +41 -0
  19. data/lib/rugged/blob.rb +28 -0
  20. data/lib/rugged/diff.rb +0 -1
  21. data/lib/rugged/diff/line.rb +1 -3
  22. data/lib/rugged/patch.rb +12 -2
  23. data/lib/rugged/repository.rb +7 -0
  24. data/lib/rugged/submodule_collection.rb +48 -0
  25. data/lib/rugged/version.rb +1 -1
  26. data/vendor/libgit2/CMakeLists.txt +27 -3
  27. data/vendor/libgit2/cmake/Modules/FindGSSAPI.cmake +324 -0
  28. data/vendor/libgit2/deps/http-parser/http_parser.h +2 -0
  29. data/vendor/libgit2/deps/zlib/adler32.c +39 -29
  30. data/vendor/libgit2/deps/zlib/crc32.c +33 -50
  31. data/vendor/libgit2/deps/zlib/crc32.h +1 -1
  32. data/vendor/libgit2/deps/zlib/deflate.c +198 -65
  33. data/vendor/libgit2/deps/zlib/deflate.h +8 -4
  34. data/vendor/libgit2/deps/zlib/infback.c +640 -0
  35. data/vendor/libgit2/deps/zlib/inffast.c +3 -3
  36. data/vendor/libgit2/deps/zlib/inffixed.h +3 -3
  37. data/vendor/libgit2/deps/zlib/inflate.c +84 -52
  38. data/vendor/libgit2/deps/zlib/inftrees.c +15 -39
  39. data/vendor/libgit2/deps/zlib/trees.c +18 -36
  40. data/vendor/libgit2/deps/zlib/zconf.h +4 -0
  41. data/vendor/libgit2/deps/zlib/zlib.h +250 -95
  42. data/vendor/libgit2/deps/zlib/zutil.c +13 -10
  43. data/vendor/libgit2/deps/zlib/zutil.h +41 -62
  44. data/vendor/libgit2/include/git2.h +4 -0
  45. data/vendor/libgit2/include/git2/annotated_commit.h +99 -0
  46. data/vendor/libgit2/include/git2/attr.h +16 -13
  47. data/vendor/libgit2/include/git2/branch.h +11 -0
  48. data/vendor/libgit2/include/git2/buffer.h +16 -0
  49. data/vendor/libgit2/include/git2/checkout.h +12 -12
  50. data/vendor/libgit2/include/git2/cherrypick.h +15 -15
  51. data/vendor/libgit2/include/git2/clone.h +77 -69
  52. data/vendor/libgit2/include/git2/common.h +13 -1
  53. data/vendor/libgit2/include/git2/config.h +0 -14
  54. data/vendor/libgit2/include/git2/describe.h +162 -0
  55. data/vendor/libgit2/include/git2/diff.h +13 -8
  56. data/vendor/libgit2/include/git2/errors.h +5 -0
  57. data/vendor/libgit2/include/git2/global.h +38 -0
  58. data/vendor/libgit2/include/git2/merge.h +38 -64
  59. data/vendor/libgit2/include/git2/net.h +2 -2
  60. data/vendor/libgit2/include/git2/notes.h +17 -0
  61. data/vendor/libgit2/include/git2/oid.h +8 -4
  62. data/vendor/libgit2/include/git2/oidarray.h +40 -0
  63. data/vendor/libgit2/include/git2/rebase.h +261 -0
  64. data/vendor/libgit2/include/git2/reflog.h +1 -1
  65. data/vendor/libgit2/include/git2/remote.h +25 -47
  66. data/vendor/libgit2/include/git2/repository.h +4 -1
  67. data/vendor/libgit2/include/git2/reset.h +10 -1
  68. data/vendor/libgit2/include/git2/revert.h +1 -1
  69. data/vendor/libgit2/include/git2/revwalk.h +28 -23
  70. data/vendor/libgit2/include/git2/status.h +19 -15
  71. data/vendor/libgit2/include/git2/submodule.h +18 -0
  72. data/vendor/libgit2/include/git2/sys/config.h +0 -1
  73. data/vendor/libgit2/{src → include/git2/sys}/hashsig.h +11 -7
  74. data/vendor/libgit2/include/git2/sys/refdb_backend.h +13 -0
  75. data/vendor/libgit2/include/git2/sys/refs.h +0 -11
  76. data/vendor/libgit2/include/git2/sys/repository.h +13 -0
  77. data/vendor/libgit2/include/git2/sys/transport.h +352 -0
  78. data/vendor/libgit2/include/git2/threads.h +10 -20
  79. data/vendor/libgit2/include/git2/transaction.h +111 -0
  80. data/vendor/libgit2/include/git2/transport.h +79 -313
  81. data/vendor/libgit2/include/git2/tree.h +4 -2
  82. data/vendor/libgit2/include/git2/types.h +77 -8
  83. data/vendor/libgit2/include/git2/version.h +2 -2
  84. data/vendor/libgit2/src/annotated_commit.c +121 -0
  85. data/vendor/libgit2/src/annotated_commit.h +22 -0
  86. data/vendor/libgit2/src/attr.c +8 -4
  87. data/vendor/libgit2/src/attr_file.c +24 -2
  88. data/vendor/libgit2/src/blame.c +0 -1
  89. data/vendor/libgit2/src/branch.c +32 -3
  90. data/vendor/libgit2/src/buf_text.c +9 -5
  91. data/vendor/libgit2/src/buf_text.h +3 -2
  92. data/vendor/libgit2/src/buffer.c +67 -10
  93. data/vendor/libgit2/src/buffer.h +4 -2
  94. data/vendor/libgit2/src/cache.c +9 -9
  95. data/vendor/libgit2/src/cache.h +1 -1
  96. data/vendor/libgit2/src/cc-compat.h +2 -0
  97. data/vendor/libgit2/src/checkout.c +263 -82
  98. data/vendor/libgit2/src/checkout.h +1 -0
  99. data/vendor/libgit2/src/cherrypick.c +41 -44
  100. data/vendor/libgit2/src/clone.c +96 -58
  101. data/vendor/libgit2/src/commit.c +5 -31
  102. data/vendor/libgit2/src/commit_list.h +3 -1
  103. data/vendor/libgit2/src/config.c +0 -17
  104. data/vendor/libgit2/src/config_cache.c +0 -2
  105. data/vendor/libgit2/src/config_file.c +12 -15
  106. data/vendor/libgit2/src/crlf.c +2 -1
  107. data/vendor/libgit2/src/describe.c +886 -0
  108. data/vendor/libgit2/src/diff.c +29 -3
  109. data/vendor/libgit2/src/diff_file.c +1 -0
  110. data/vendor/libgit2/src/diff_patch.c +2 -3
  111. data/vendor/libgit2/src/diff_print.c +11 -9
  112. data/vendor/libgit2/src/diff_tform.c +4 -4
  113. data/vendor/libgit2/src/errors.c +9 -7
  114. data/vendor/libgit2/src/fetch.c +6 -6
  115. data/vendor/libgit2/src/fetchhead.h +2 -4
  116. data/vendor/libgit2/src/filebuf.c +0 -2
  117. data/vendor/libgit2/src/filebuf.h +2 -3
  118. data/vendor/libgit2/src/fileops.c +9 -7
  119. data/vendor/libgit2/src/global.c +44 -35
  120. data/vendor/libgit2/src/global.h +2 -0
  121. data/vendor/libgit2/src/graph.c +2 -2
  122. data/vendor/libgit2/src/hash.h +3 -1
  123. data/vendor/libgit2/src/hash/hash_common_crypto.h +44 -0
  124. data/vendor/libgit2/src/hash/hash_win32.c +1 -1
  125. data/vendor/libgit2/src/hashsig.c +1 -1
  126. data/vendor/libgit2/src/ignore.c +5 -88
  127. data/vendor/libgit2/src/index.c +70 -57
  128. data/vendor/libgit2/src/index.h +1 -0
  129. data/vendor/libgit2/src/indexer.c +16 -5
  130. data/vendor/libgit2/src/iterator.c +70 -1
  131. data/vendor/libgit2/src/iterator.h +5 -1
  132. data/vendor/libgit2/src/map.h +0 -1
  133. data/vendor/libgit2/src/merge.c +203 -327
  134. data/vendor/libgit2/src/merge.h +3 -13
  135. data/vendor/libgit2/src/mwindow.c +119 -8
  136. data/vendor/libgit2/src/mwindow.h +9 -1
  137. data/vendor/libgit2/src/netops.c +7 -8
  138. data/vendor/libgit2/src/netops.h +6 -16
  139. data/vendor/libgit2/src/notes.c +31 -4
  140. data/vendor/libgit2/src/notes.h +3 -0
  141. data/vendor/libgit2/src/odb.c +23 -1
  142. data/vendor/libgit2/src/odb_loose.c +1 -1
  143. data/vendor/libgit2/src/odb_pack.c +6 -3
  144. data/vendor/libgit2/src/oid.c +9 -1
  145. data/vendor/libgit2/src/oid.h +11 -0
  146. data/vendor/libgit2/src/oidarray.c +21 -0
  147. data/vendor/libgit2/src/oidarray.h +18 -0
  148. data/vendor/libgit2/src/oidmap.h +16 -0
  149. data/vendor/libgit2/src/pack.c +20 -7
  150. data/vendor/libgit2/src/pack.h +3 -0
  151. data/vendor/libgit2/src/path.c +120 -293
  152. data/vendor/libgit2/src/path.h +21 -44
  153. data/vendor/libgit2/src/pathspec.c +1 -1
  154. data/vendor/libgit2/src/pool.c +5 -11
  155. data/vendor/libgit2/src/pool.h +0 -2
  156. data/vendor/libgit2/src/posix.c +6 -6
  157. data/vendor/libgit2/src/posix.h +48 -28
  158. data/vendor/libgit2/src/push.c +19 -48
  159. data/vendor/libgit2/src/push.h +2 -4
  160. data/vendor/libgit2/src/rebase.c +1125 -0
  161. data/vendor/libgit2/src/refdb.c +19 -0
  162. data/vendor/libgit2/src/refdb.h +2 -1
  163. data/vendor/libgit2/src/refdb_fs.c +101 -29
  164. data/vendor/libgit2/src/reflog.c +1 -1
  165. data/vendor/libgit2/src/refs.c +38 -3
  166. data/vendor/libgit2/src/refs.h +13 -2
  167. data/vendor/libgit2/src/refspec.c +20 -2
  168. data/vendor/libgit2/src/remote.c +288 -154
  169. data/vendor/libgit2/src/remote.h +5 -1
  170. data/vendor/libgit2/src/repository.c +75 -36
  171. data/vendor/libgit2/src/repository.h +3 -25
  172. data/vendor/libgit2/src/reset.c +5 -1
  173. data/vendor/libgit2/src/revert.c +4 -6
  174. data/vendor/libgit2/src/revparse.c +15 -18
  175. data/vendor/libgit2/src/revwalk.c +96 -22
  176. data/vendor/libgit2/src/revwalk.h +5 -4
  177. data/vendor/libgit2/src/settings.c +22 -0
  178. data/vendor/libgit2/src/signature.c +37 -2
  179. data/vendor/libgit2/src/signature.h +3 -0
  180. data/vendor/libgit2/src/stash.c +17 -12
  181. data/vendor/libgit2/src/status.c +13 -3
  182. data/vendor/libgit2/src/strnlen.h +2 -1
  183. data/vendor/libgit2/src/submodule.c +75 -35
  184. data/vendor/libgit2/src/thread-utils.h +4 -9
  185. data/vendor/libgit2/src/trace.h +9 -1
  186. data/vendor/libgit2/src/transaction.c +352 -0
  187. data/vendor/libgit2/src/transport.c +91 -97
  188. data/vendor/libgit2/src/transports/auth.c +71 -0
  189. data/vendor/libgit2/src/transports/auth.h +63 -0
  190. data/vendor/libgit2/src/transports/auth_negotiate.c +275 -0
  191. data/vendor/libgit2/src/transports/auth_negotiate.h +27 -0
  192. data/vendor/libgit2/src/transports/cred.c +58 -0
  193. data/vendor/libgit2/src/transports/cred.h +14 -0
  194. data/vendor/libgit2/src/transports/cred_helpers.c +3 -0
  195. data/vendor/libgit2/src/transports/git.c +1 -0
  196. data/vendor/libgit2/src/transports/http.c +208 -82
  197. data/vendor/libgit2/src/transports/local.c +2 -2
  198. data/vendor/libgit2/src/transports/smart.c +2 -0
  199. data/vendor/libgit2/src/transports/smart.h +2 -0
  200. data/vendor/libgit2/src/transports/smart_protocol.c +10 -10
  201. data/vendor/libgit2/src/transports/ssh.c +243 -57
  202. data/vendor/libgit2/src/transports/winhttp.c +139 -35
  203. data/vendor/libgit2/src/tree-cache.c +118 -31
  204. data/vendor/libgit2/src/tree-cache.h +12 -7
  205. data/vendor/libgit2/src/tree.c +83 -64
  206. data/vendor/libgit2/src/tree.h +2 -3
  207. data/vendor/libgit2/src/unix/map.c +8 -2
  208. data/vendor/libgit2/src/unix/posix.h +23 -9
  209. data/vendor/libgit2/src/unix/realpath.c +8 -7
  210. data/vendor/libgit2/src/userdiff.h +3 -3
  211. data/vendor/libgit2/src/util.c +2 -92
  212. data/vendor/libgit2/src/util.h +3 -15
  213. data/vendor/libgit2/src/win32/findfile.c +0 -1
  214. data/vendor/libgit2/src/win32/map.c +3 -2
  215. data/vendor/libgit2/src/win32/mingw-compat.h +5 -12
  216. data/vendor/libgit2/src/win32/msvc-compat.h +3 -32
  217. data/vendor/libgit2/src/win32/posix.h +20 -32
  218. data/vendor/libgit2/src/win32/posix_w32.c +103 -31
  219. data/vendor/libgit2/src/win32/utf-conv.c +6 -36
  220. data/vendor/libgit2/src/win32/utf-conv.h +39 -0
  221. data/vendor/libgit2/src/win32/w32_util.h +0 -1
  222. metadata +32 -7
  223. data/vendor/libgit2/src/win32/path_w32.c +0 -305
  224. data/vendor/libgit2/src/win32/path_w32.h +0 -82
@@ -16,6 +16,7 @@
16
16
  #include "commit.h"
17
17
  #include "signature.h"
18
18
  #include "message.h"
19
+ #include "refs.h"
19
20
 
20
21
  void git_commit__free(void *_commit)
21
22
  {
@@ -34,35 +35,6 @@ void git_commit__free(void *_commit)
34
35
  git__free(commit);
35
36
  }
36
37
 
37
- static int update_ref_for_commit(git_repository *repo, git_reference *ref, const char *update_ref, const git_oid *id, const git_signature *committer)
38
- {
39
- git_reference *ref2 = NULL;
40
- int error;
41
- git_commit *c;
42
- const char *shortmsg;
43
- git_buf reflog_msg = GIT_BUF_INIT;
44
-
45
- if ((error = git_commit_lookup(&c, repo, id)) < 0) {
46
- return error;
47
- }
48
-
49
- shortmsg = git_commit_summary(c);
50
- git_buf_printf(&reflog_msg, "commit%s: %s",
51
- git_commit_parentcount(c) == 0 ? " (initial)" : "",
52
- shortmsg);
53
- git_commit_free(c);
54
-
55
- if (ref) {
56
- error = git_reference_set_target(&ref2, ref, id, committer, git_buf_cstr(&reflog_msg));
57
- git_reference_free(ref2);
58
- } else {
59
- error = git_reference__update_terminal(repo, update_ref, id, committer, git_buf_cstr(&reflog_msg));
60
- }
61
-
62
- git_buf_free(&reflog_msg);
63
- return error;
64
- }
65
-
66
38
  int git_commit_create_from_callback(
67
39
  git_oid *id,
68
40
  git_repository *repo,
@@ -131,7 +103,8 @@ int git_commit_create_from_callback(
131
103
  git_buf_free(&commit);
132
104
 
133
105
  if (update_ref != NULL) {
134
- error = update_ref_for_commit(repo, ref, update_ref, id, committer);
106
+ error = git_reference__update_for_commit(
107
+ repo, ref, update_ref, id, committer, "commit");
135
108
  git_reference_free(ref);
136
109
  return error;
137
110
  }
@@ -321,7 +294,8 @@ int git_commit_amend(
321
294
  &tree_id, commit_parent_for_amend, (void *)commit_to_amend);
322
295
 
323
296
  if (!error && update_ref) {
324
- error = update_ref_for_commit(repo, ref, NULL, id, committer);
297
+ error = git_reference__update_for_commit(
298
+ repo, ref, NULL, id, committer, "commit");
325
299
  git_reference_free(ref);
326
300
  }
327
301
 
@@ -18,6 +18,8 @@
18
18
  #define COMMIT_ALLOC \
19
19
  (sizeof(git_commit_list_node) + PARENTS_PER_COMMIT * sizeof(git_commit_list_node *))
20
20
 
21
+ #define FLAG_BITS 4
22
+
21
23
  typedef struct git_commit_list_node {
22
24
  git_oid oid;
23
25
  uint32_t time;
@@ -25,7 +27,7 @@ typedef struct git_commit_list_node {
25
27
  uninteresting:1,
26
28
  topo_delay:1,
27
29
  parsed:1,
28
- flags : 4;
30
+ flags : FLAG_BITS;
29
31
 
30
32
  unsigned short in_degree;
31
33
  unsigned short out_degree;
@@ -326,23 +326,6 @@ int git_config_add_backend(
326
326
  return 0;
327
327
  }
328
328
 
329
- int git_config_refresh(git_config *cfg)
330
- {
331
- int error = 0;
332
- size_t i;
333
-
334
- for (i = 0; i < cfg->files.length && !error; ++i) {
335
- file_internal *internal = git_vector_get(&cfg->files, i);
336
- git_config_backend *file = internal->file;
337
- error = file->refresh(file);
338
- }
339
-
340
- if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
341
- git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
342
-
343
- return error;
344
- }
345
-
346
329
  /*
347
330
  * Loop over all the variables
348
331
  */
@@ -76,8 +76,6 @@ static struct map_data _cvar_maps[] = {
76
76
  {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
77
77
  {"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT},
78
78
  {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT },
79
- {"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT },
80
- {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT },
81
79
  };
82
80
 
83
81
  int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
@@ -698,7 +698,6 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
698
698
  backend->header.parent.del = config_delete;
699
699
  backend->header.parent.del_multivar = config_delete_multivar;
700
700
  backend->header.parent.iterator = config_iterator_new;
701
- backend->header.parent.refresh = config_refresh;
702
701
  backend->header.parent.snapshot = config_snapshot;
703
702
  backend->header.parent.free = backend_free;
704
703
 
@@ -744,13 +743,6 @@ static int config_delete_readonly(git_config_backend *cfg, const char *name)
744
743
  return config_error_readonly();
745
744
  }
746
745
 
747
- static int config_refresh_readonly(git_config_backend *cfg)
748
- {
749
- GIT_UNUSED(cfg);
750
-
751
- return config_error_readonly();
752
- }
753
-
754
746
  static void backend_readonly_free(git_config_backend *_backend)
755
747
  {
756
748
  diskfile_backend *backend = (diskfile_backend *)_backend;
@@ -804,7 +796,6 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
804
796
  backend->header.parent.del = config_delete_readonly;
805
797
  backend->header.parent.del_multivar = config_delete_multivar_readonly;
806
798
  backend->header.parent.iterator = config_iterator_new;
807
- backend->header.parent.refresh = config_refresh_readonly;
808
799
  backend->header.parent.free = backend_readonly_free;
809
800
 
810
801
  *out = (git_config_backend *)backend;
@@ -1278,7 +1269,7 @@ static int config_parse(git_strmap *values, diskfile_backend *cfg_file, struct r
1278
1269
  if ((result = git_path_dirname_r(&path, reader->file_path)) < 0)
1279
1270
  break;
1280
1271
 
1281
- /* We need to know out index in the array, as the next config_parse call may realloc */
1272
+ /* We need to know our index in the array, as the next config_parse call may realloc */
1282
1273
  index = git_array_size(cfg_file->readers) - 1;
1283
1274
  dir = git_buf_detach(&path);
1284
1275
  result = included_path(&path, dir, var->entry->value);
@@ -1289,12 +1280,18 @@ static int config_parse(git_strmap *values, diskfile_backend *cfg_file, struct r
1289
1280
 
1290
1281
  r->file_path = git_buf_detach(&path);
1291
1282
  git_buf_init(&r->buffer, 0);
1292
- if ((result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime,
1293
- &r->file_size, NULL)) < 0)
1294
- break;
1283
+ result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime,
1284
+ &r->file_size, NULL);
1285
+
1286
+ if (result == 0) {
1287
+ result = config_parse(values, cfg_file, r, level, depth+1);
1288
+ r = git_array_get(cfg_file->readers, index);
1289
+ }
1290
+ else if (result == GIT_ENOTFOUND) {
1291
+ giterr_clear();
1292
+ result = 0;
1293
+ }
1295
1294
 
1296
- result = config_parse(values, cfg_file, r, level, depth+1);
1297
- r = git_array_get(cfg_file->readers, index);
1298
1295
  git_buf_free(&r->buffer);
1299
1296
 
1300
1297
  if (result < 0)
@@ -286,7 +286,8 @@ static int crlf_check(
286
286
  if (error < 0)
287
287
  return error;
288
288
 
289
- if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
289
+ if (ca.crlf_action == GIT_CRLF_GUESS &&
290
+ ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
290
291
  return GIT_PASSTHROUGH;
291
292
 
292
293
  if (ca.auto_crlf == GIT_AUTO_CRLF_INPUT &&
@@ -0,0 +1,886 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #include "git2/describe.h"
8
+ #include "git2/strarray.h"
9
+ #include "git2/diff.h"
10
+ #include "git2/status.h"
11
+
12
+ #include "common.h"
13
+ #include "commit.h"
14
+ #include "commit_list.h"
15
+ #include "oidmap.h"
16
+ #include "refs.h"
17
+ #include "revwalk.h"
18
+ #include "tag.h"
19
+ #include "vector.h"
20
+ #include "repository.h"
21
+
22
+ /* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */
23
+
24
+ struct commit_name {
25
+ git_tag *tag;
26
+ unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
27
+ unsigned name_checked:1;
28
+ git_oid sha1;
29
+ char *path;
30
+
31
+ /* Khash workaround. They original key has to still be reachable */
32
+ git_oid peeled;
33
+ };
34
+
35
+ static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key)
36
+ {
37
+ khint_t pos = git_oidmap_lookup_index(map, key);
38
+
39
+ if (!git_oidmap_valid_index(map, pos))
40
+ return NULL;
41
+
42
+ return git_oidmap_value_at(map, pos);
43
+ }
44
+
45
+ static struct commit_name *find_commit_name(
46
+ git_oidmap *names,
47
+ const git_oid *peeled)
48
+ {
49
+ return (struct commit_name *)(oidmap_value_bykey(names, peeled));
50
+ }
51
+
52
+ static int replace_name(
53
+ git_tag **tag,
54
+ git_repository *repo,
55
+ struct commit_name *e,
56
+ unsigned int prio,
57
+ const git_oid *sha1)
58
+ {
59
+ git_time_t e_time = 0, t_time = 0;
60
+
61
+ if (!e || e->prio < prio)
62
+ return 1;
63
+
64
+ if (e->prio == 2 && prio == 2) {
65
+ /* Multiple annotated tags point to the same commit.
66
+ * Select one to keep based upon their tagger date.
67
+ */
68
+ git_tag *t = NULL;
69
+
70
+ if (!e->tag) {
71
+ if (git_tag_lookup(&t, repo, &e->sha1) < 0)
72
+ return 1;
73
+ e->tag = t;
74
+ }
75
+
76
+ if (git_tag_lookup(&t, repo, sha1) < 0)
77
+ return 0;
78
+
79
+ *tag = t;
80
+
81
+ if (e->tag->tagger)
82
+ e_time = e->tag->tagger->when.time;
83
+
84
+ if (t->tagger)
85
+ t_time = t->tagger->when.time;
86
+
87
+ if (e_time < t_time)
88
+ return 1;
89
+ }
90
+
91
+ return 0;
92
+ }
93
+
94
+ static int add_to_known_names(
95
+ git_repository *repo,
96
+ git_oidmap *names,
97
+ const char *path,
98
+ const git_oid *peeled,
99
+ unsigned int prio,
100
+ const git_oid *sha1)
101
+ {
102
+ struct commit_name *e = find_commit_name(names, peeled);
103
+ bool found = (e != NULL);
104
+
105
+ git_tag *tag = NULL;
106
+ if (replace_name(&tag, repo, e, prio, sha1)) {
107
+ if (!found) {
108
+ e = git__malloc(sizeof(struct commit_name));
109
+ GITERR_CHECK_ALLOC(e);
110
+
111
+ e->path = NULL;
112
+ e->tag = NULL;
113
+ }
114
+
115
+ if (e->tag)
116
+ git_tag_free(e->tag);
117
+ e->tag = tag;
118
+ e->prio = prio;
119
+ e->name_checked = 0;
120
+ git_oid_cpy(&e->sha1, sha1);
121
+ git__free(e->path);
122
+ e->path = git__strdup(path);
123
+ git_oid_cpy(&e->peeled, peeled);
124
+
125
+ if (!found) {
126
+ int ret;
127
+
128
+ git_oidmap_insert(names, &e->peeled, e, ret);
129
+ if (ret < 0)
130
+ return -1;
131
+ }
132
+ }
133
+ else
134
+ git_tag_free(tag);
135
+
136
+ return 0;
137
+ }
138
+
139
+ static int retrieve_peeled_tag_or_object_oid(
140
+ git_oid *peeled_out,
141
+ git_oid *ref_target_out,
142
+ git_repository *repo,
143
+ const char *refname)
144
+ {
145
+ git_reference *ref;
146
+ git_object *peeled = NULL;
147
+ int error;
148
+
149
+ if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0)
150
+ return error;
151
+
152
+ if ((error = git_reference_peel(&peeled, ref, GIT_OBJ_ANY)) < 0)
153
+ goto cleanup;
154
+
155
+ git_oid_cpy(ref_target_out, git_reference_target(ref));
156
+ git_oid_cpy(peeled_out, git_object_id(peeled));
157
+
158
+ if (git_oid_cmp(ref_target_out, peeled_out) != 0)
159
+ error = 1; /* The reference was pointing to a annotated tag */
160
+ else
161
+ error = 0; /* Any other object */
162
+
163
+ cleanup:
164
+ git_reference_free(ref);
165
+ git_object_free(peeled);
166
+ return error;
167
+ }
168
+
169
+ struct git_describe_result {
170
+ int dirty;
171
+ int exact_match;
172
+ int fallback_to_id;
173
+ git_oid commit_id;
174
+ git_repository *repo;
175
+ struct commit_name *name;
176
+ struct possible_tag *tag;
177
+ };
178
+
179
+ struct get_name_data
180
+ {
181
+ git_describe_options *opts;
182
+ git_repository *repo;
183
+ git_oidmap *names;
184
+ git_describe_result *result;
185
+ };
186
+
187
+ static int commit_name_dup(struct commit_name **out, struct commit_name *in)
188
+ {
189
+ struct commit_name *name;
190
+
191
+ name = git__malloc(sizeof(struct commit_name));
192
+ GITERR_CHECK_ALLOC(name);
193
+
194
+ memcpy(name, in, sizeof(struct commit_name));
195
+ name->tag = NULL;
196
+ name->path = NULL;
197
+
198
+ if (in->tag && git_object_dup((git_object **) &name->tag, (git_object *) in->tag) < 0)
199
+ return -1;
200
+
201
+ name->path = git__strdup(in->path);
202
+ GITERR_CHECK_ALLOC(name->path);
203
+
204
+ *out = name;
205
+ return 0;
206
+ }
207
+
208
+ static int get_name(const char *refname, void *payload)
209
+ {
210
+ struct get_name_data *data;
211
+ bool is_tag, is_annotated, all;
212
+ git_oid peeled, sha1;
213
+ unsigned int prio;
214
+ int error = 0;
215
+
216
+ data = (struct get_name_data *)payload;
217
+ is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR);
218
+ all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
219
+
220
+ /* Reject anything outside refs/tags/ unless --all */
221
+ if (!all && !is_tag)
222
+ return 0;
223
+
224
+ /* Accept only tags that match the pattern, if given */
225
+ if (data->opts->pattern && (!is_tag || p_fnmatch(data->opts->pattern,
226
+ refname + strlen(GIT_REFS_TAGS_DIR), 0)))
227
+ return 0;
228
+
229
+ /* Is it annotated? */
230
+ if ((error = retrieve_peeled_tag_or_object_oid(
231
+ &peeled, &sha1, data->repo, refname)) < 0)
232
+ return error;
233
+
234
+ is_annotated = error;
235
+
236
+ /*
237
+ * By default, we only use annotated tags, but with --tags
238
+ * we fall back to lightweight ones (even without --tags,
239
+ * we still remember lightweight ones, only to give hints
240
+ * in an error message). --all allows any refs to be used.
241
+ */
242
+ if (is_annotated)
243
+ prio = 2;
244
+ else if (is_tag)
245
+ prio = 1;
246
+ else
247
+ prio = 0;
248
+
249
+ add_to_known_names(data->repo, data->names,
250
+ all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR),
251
+ &peeled, prio, &sha1);
252
+ return 0;
253
+ }
254
+
255
+ struct possible_tag {
256
+ struct commit_name *name;
257
+ int depth;
258
+ int found_order;
259
+ unsigned flag_within;
260
+ };
261
+
262
+ static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in)
263
+ {
264
+ struct possible_tag *tag;
265
+
266
+ tag = git__malloc(sizeof(struct possible_tag));
267
+ GITERR_CHECK_ALLOC(tag);
268
+
269
+ memcpy(tag, in, sizeof(struct possible_tag));
270
+ tag->name = NULL;
271
+
272
+ if (commit_name_dup(&tag->name, in->name) < 0)
273
+ return -1;
274
+
275
+ *out = tag;
276
+ return 0;
277
+ }
278
+
279
+ static int compare_pt(const void *a_, const void *b_)
280
+ {
281
+ struct possible_tag *a = (struct possible_tag *)a_;
282
+ struct possible_tag *b = (struct possible_tag *)b_;
283
+ if (a->depth != b->depth)
284
+ return a->depth - b->depth;
285
+ if (a->found_order != b->found_order)
286
+ return a->found_order - b->found_order;
287
+ return 0;
288
+ }
289
+
290
+ #define SEEN (1u << 0)
291
+
292
+ static unsigned long finish_depth_computation(
293
+ git_pqueue *list,
294
+ git_revwalk *walk,
295
+ struct possible_tag *best)
296
+ {
297
+ unsigned long seen_commits = 0;
298
+ int error, i;
299
+
300
+ while (git_pqueue_size(list) > 0) {
301
+ git_commit_list_node *c = git_pqueue_pop(list);
302
+ seen_commits++;
303
+ if (c->flags & best->flag_within) {
304
+ size_t index = 0;
305
+ while (git_pqueue_size(list) > index) {
306
+ git_commit_list_node *i = git_pqueue_get(list, index);
307
+ if (!(i->flags & best->flag_within))
308
+ break;
309
+ index++;
310
+ }
311
+ if (index > git_pqueue_size(list))
312
+ break;
313
+ } else
314
+ best->depth++;
315
+ for (i = 0; i < c->out_degree; i++) {
316
+ git_commit_list_node *p = c->parents[i];
317
+ if ((error = git_commit_list_parse(walk, p)) < 0)
318
+ return error;
319
+ if (!(p->flags & SEEN))
320
+ if ((error = git_pqueue_insert(list, p)) < 0)
321
+ return error;
322
+ p->flags |= c->flags;
323
+ }
324
+ }
325
+ return seen_commits;
326
+ }
327
+
328
+ static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n)
329
+ {
330
+ if (n->prio == 2 && !n->tag) {
331
+ if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) {
332
+ giterr_set(GITERR_TAG, "Annotated tag '%s' not available", n->path);
333
+ return -1;
334
+ }
335
+ }
336
+
337
+ if (n->tag && !n->name_checked) {
338
+ if (!git_tag_name(n->tag)) {
339
+ giterr_set(GITERR_TAG, "Annotated tag '%s' has no embedded name", n->path);
340
+ return -1;
341
+ }
342
+
343
+ /* TODO: Cope with warnings
344
+ if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
345
+ warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
346
+ */
347
+
348
+ n->name_checked = 1;
349
+ }
350
+
351
+ if (n->tag)
352
+ git_buf_printf(buf, "%s", git_tag_name(n->tag));
353
+ else
354
+ git_buf_printf(buf, "%s", n->path);
355
+
356
+ return 0;
357
+ }
358
+
359
+ static int find_unique_abbrev_size(
360
+ int *out,
361
+ git_repository *repo,
362
+ const git_oid *oid_in,
363
+ int abbreviated_size)
364
+ {
365
+ size_t size = abbreviated_size;
366
+ git_odb *odb;
367
+ git_oid dummy;
368
+ int error;
369
+
370
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
371
+ return error;
372
+
373
+ while (size < GIT_OID_HEXSZ) {
374
+ if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) {
375
+ *out = (int) size;
376
+ return 0;
377
+ }
378
+
379
+ /* If the error wasn't that it's not unique, then it's a proper error */
380
+ if (error != GIT_EAMBIGUOUS)
381
+ return error;
382
+
383
+ /* Try again with a larger size */
384
+ size++;
385
+ }
386
+
387
+ /* If we didn't find any shorter prefix, we have to do the whole thing */
388
+ *out = GIT_OID_HEXSZ;
389
+
390
+ return 0;
391
+ }
392
+
393
+ static int show_suffix(
394
+ git_buf *buf,
395
+ int depth,
396
+ git_repository *repo,
397
+ const git_oid* id,
398
+ size_t abbrev_size)
399
+ {
400
+ int error, size = 0;
401
+
402
+ char hex_oid[GIT_OID_HEXSZ];
403
+
404
+ if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0)
405
+ return error;
406
+
407
+ git_oid_fmt(hex_oid, id);
408
+
409
+ git_buf_printf(buf, "-%d-g", depth);
410
+
411
+ git_buf_put(buf, hex_oid, size);
412
+
413
+ return git_buf_oom(buf) ? -1 : 0;
414
+ }
415
+
416
+ #define MAX_CANDIDATES_TAGS FLAG_BITS - 1
417
+
418
+ static int describe_not_found(const git_oid *oid, const char *message_format) {
419
+ char oid_str[GIT_OID_HEXSZ + 1];
420
+ git_oid_tostr(oid_str, sizeof(oid_str), oid);
421
+
422
+ giterr_set(GITERR_DESCRIBE, message_format, oid_str);
423
+ return GIT_ENOTFOUND;
424
+ }
425
+
426
+ static int describe(
427
+ struct get_name_data *data,
428
+ git_commit *commit)
429
+ {
430
+ struct commit_name *n;
431
+ struct possible_tag *best;
432
+ bool all, tags;
433
+ git_revwalk *walk = NULL;
434
+ git_pqueue list;
435
+ git_commit_list_node *cmit, *gave_up_on = NULL;
436
+ git_vector all_matches = GIT_VECTOR_INIT;
437
+ unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
438
+ unsigned long seen_commits = 0; /* TODO: Check long */
439
+ unsigned int unannotated_cnt = 0;
440
+ int error;
441
+
442
+ if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0)
443
+ return -1;
444
+
445
+ if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0)
446
+ goto cleanup;
447
+
448
+ all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
449
+ tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS;
450
+
451
+ git_oid_cpy(&data->result->commit_id, git_commit_id(commit));
452
+
453
+ n = find_commit_name(data->names, git_commit_id(commit));
454
+ if (n && (tags || all || n->prio == 2)) {
455
+ /*
456
+ * Exact match to an existing ref.
457
+ */
458
+ data->result->exact_match = 1;
459
+ if ((error = commit_name_dup(&data->result->name, n)) < 0)
460
+ goto cleanup;
461
+
462
+ goto cleanup;
463
+ }
464
+
465
+ if (!data->opts->max_candidates_tags) {
466
+ error = describe_not_found(
467
+ git_commit_id(commit),
468
+ "Cannot describe - no tag exactly matches '%s'");
469
+
470
+ goto cleanup;
471
+ }
472
+
473
+ if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0)
474
+ goto cleanup;
475
+
476
+ if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL)
477
+ goto cleanup;
478
+
479
+ if ((error = git_commit_list_parse(walk, cmit)) < 0)
480
+ goto cleanup;
481
+
482
+ cmit->flags = SEEN;
483
+
484
+ if ((error = git_pqueue_insert(&list, cmit)) < 0)
485
+ goto cleanup;
486
+
487
+ while (git_pqueue_size(&list) > 0)
488
+ {
489
+ int i;
490
+
491
+ git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list);
492
+ seen_commits++;
493
+
494
+ n = find_commit_name(data->names, &c->oid);
495
+
496
+ if (n) {
497
+ if (!tags && !all && n->prio < 2) {
498
+ unannotated_cnt++;
499
+ } else if (match_cnt < data->opts->max_candidates_tags) {
500
+ struct possible_tag *t = git__malloc(sizeof(struct commit_name));
501
+ GITERR_CHECK_ALLOC(t);
502
+ if ((error = git_vector_insert(&all_matches, t)) < 0)
503
+ goto cleanup;
504
+
505
+ match_cnt++;
506
+
507
+ t->name = n;
508
+ t->depth = seen_commits - 1;
509
+ t->flag_within = 1u << match_cnt;
510
+ t->found_order = match_cnt;
511
+ c->flags |= t->flag_within;
512
+ if (n->prio == 2)
513
+ annotated_cnt++;
514
+ }
515
+ else {
516
+ gave_up_on = c;
517
+ break;
518
+ }
519
+ }
520
+
521
+ for (cur_match = 0; cur_match < match_cnt; cur_match++) {
522
+ struct possible_tag *t = git_vector_get(&all_matches, cur_match);
523
+ if (!(c->flags & t->flag_within))
524
+ t->depth++;
525
+ }
526
+
527
+ if (annotated_cnt && (git_pqueue_size(&list) == 0)) {
528
+ /*
529
+ if (debug) {
530
+ char oid_str[GIT_OID_HEXSZ + 1];
531
+ git_oid_tostr(oid_str, sizeof(oid_str), &c->oid);
532
+
533
+ fprintf(stderr, "finished search at %s\n", oid_str);
534
+ }
535
+ */
536
+ break;
537
+ }
538
+ for (i = 0; i < c->out_degree; i++) {
539
+ git_commit_list_node *p = c->parents[i];
540
+ if ((error = git_commit_list_parse(walk, p)) < 0)
541
+ goto cleanup;
542
+ if (!(p->flags & SEEN))
543
+ if ((error = git_pqueue_insert(&list, p)) < 0)
544
+ goto cleanup;
545
+ p->flags |= c->flags;
546
+
547
+ if (data->opts->only_follow_first_parent)
548
+ break;
549
+ }
550
+ }
551
+
552
+ if (!match_cnt) {
553
+ if (data->opts->show_commit_oid_as_fallback) {
554
+ data->result->fallback_to_id = 1;
555
+ git_oid_cpy(&data->result->commit_id, &cmit->oid);
556
+
557
+ goto cleanup;
558
+ }
559
+ if (unannotated_cnt) {
560
+ error = describe_not_found(git_commit_id(commit),
561
+ "Cannot describe - "
562
+ "No annotated tags can describe '%s'."
563
+ "However, there were unannotated tags.");
564
+ goto cleanup;
565
+ }
566
+ else {
567
+ error = describe_not_found(git_commit_id(commit),
568
+ "Cannot describe - "
569
+ "No tags can describe '%s'.");
570
+ goto cleanup;
571
+ }
572
+ }
573
+
574
+ git_vector_sort(&all_matches);
575
+
576
+ best = (struct possible_tag *)git_vector_get(&all_matches, 0);
577
+
578
+ if (gave_up_on) {
579
+ git_pqueue_insert(&list, gave_up_on);
580
+ seen_commits--;
581
+ }
582
+ if ((error = finish_depth_computation(
583
+ &list, walk, best)) < 0)
584
+ goto cleanup;
585
+
586
+ seen_commits += error;
587
+ if ((error = possible_tag_dup(&data->result->tag, best)) < 0)
588
+ goto cleanup;
589
+
590
+ /*
591
+ {
592
+ static const char *prio_names[] = {
593
+ "head", "lightweight", "annotated",
594
+ };
595
+
596
+ char oid_str[GIT_OID_HEXSZ + 1];
597
+
598
+ if (debug) {
599
+ for (cur_match = 0; cur_match < match_cnt; cur_match++) {
600
+ struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match);
601
+ fprintf(stderr, " %-11s %8d %s\n",
602
+ prio_names[t->name->prio],
603
+ t->depth, t->name->path);
604
+ }
605
+ fprintf(stderr, "traversed %lu commits\n", seen_commits);
606
+ if (gave_up_on) {
607
+ git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid);
608
+ fprintf(stderr,
609
+ "more than %i tags found; listed %i most recent\n"
610
+ "gave up search at %s\n",
611
+ data->opts->max_candidates_tags, data->opts->max_candidates_tags,
612
+ oid_str);
613
+ }
614
+ }
615
+ }
616
+ */
617
+
618
+ git_oid_cpy(&data->result->commit_id, &cmit->oid);
619
+
620
+ cleanup:
621
+ {
622
+ size_t i;
623
+ struct possible_tag *match;
624
+ git_vector_foreach(&all_matches, i, match) {
625
+ git__free(match);
626
+ }
627
+ }
628
+ git_vector_free(&all_matches);
629
+ git_pqueue_free(&list);
630
+ git_revwalk_free(walk);
631
+ return error;
632
+ }
633
+
634
+ static int normalize_options(
635
+ git_describe_options *dst,
636
+ const git_describe_options *src)
637
+ {
638
+ git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT;
639
+ if (!src) src = &default_options;
640
+
641
+ *dst = *src;
642
+
643
+ if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS)
644
+ dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS;
645
+
646
+ return 0;
647
+ }
648
+
649
+ int git_describe_commit(
650
+ git_describe_result **result,
651
+ git_object *committish,
652
+ git_describe_options *opts)
653
+ {
654
+ struct get_name_data data;
655
+ struct commit_name *name;
656
+ git_commit *commit;
657
+ int error = -1;
658
+ git_describe_options normalized;
659
+
660
+ assert(committish);
661
+
662
+ data.result = git__calloc(1, sizeof(git_describe_result));
663
+ GITERR_CHECK_ALLOC(data.result);
664
+ data.result->repo = git_object_owner(committish);
665
+
666
+ data.repo = git_object_owner(committish);
667
+
668
+ if ((error = normalize_options(&normalized, opts)) < 0)
669
+ return error;
670
+
671
+ GITERR_CHECK_VERSION(
672
+ &normalized,
673
+ GIT_DESCRIBE_OPTIONS_VERSION,
674
+ "git_describe_options");
675
+ data.opts = &normalized;
676
+
677
+ data.names = git_oidmap_alloc();
678
+ GITERR_CHECK_ALLOC(data.names);
679
+
680
+ /** TODO: contains to be implemented */
681
+
682
+ if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJ_COMMIT)) < 0)
683
+ goto cleanup;
684
+
685
+ if (git_reference_foreach_name(
686
+ git_object_owner(committish),
687
+ get_name, &data) < 0)
688
+ goto cleanup;
689
+
690
+ if (git_oidmap_size(data.names) == 0) {
691
+ giterr_set(GITERR_DESCRIBE, "Cannot describe - "
692
+ "No reference found, cannot describe anything.");
693
+ error = -1;
694
+ goto cleanup;
695
+ }
696
+
697
+ if ((error = describe(&data, commit)) < 0)
698
+ goto cleanup;
699
+
700
+ cleanup:
701
+ git_commit_free(commit);
702
+
703
+ git_oidmap_foreach_value(data.names, name, {
704
+ git_tag_free(name->tag);
705
+ git__free(name->path);
706
+ git__free(name);
707
+ });
708
+
709
+ git_oidmap_free(data.names);
710
+
711
+ if (error < 0)
712
+ git_describe_result_free(data.result);
713
+ else
714
+ *result = data.result;
715
+
716
+ return error;
717
+ }
718
+
719
+ int git_describe_workdir(
720
+ git_describe_result **out,
721
+ git_repository *repo,
722
+ git_describe_options *opts)
723
+ {
724
+ int error;
725
+ git_oid current_id;
726
+ git_status_list *status = NULL;
727
+ git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
728
+ git_describe_result *result = NULL;
729
+ git_object *commit;
730
+
731
+ if ((error = git_reference_name_to_id(&current_id, repo, GIT_HEAD_FILE)) < 0)
732
+ return error;
733
+
734
+ if ((error = git_object_lookup(&commit, repo, &current_id, GIT_OBJ_COMMIT)) < 0)
735
+ return error;
736
+
737
+ /* The first step is to perform a describe of HEAD, so we can leverage this */
738
+ if ((error = git_describe_commit(&result, commit, opts)) < 0)
739
+ goto out;
740
+
741
+ if ((error = git_status_list_new(&status, repo, &status_opts)) < 0)
742
+ goto out;
743
+
744
+
745
+ if (git_status_list_entrycount(status) > 0)
746
+ result->dirty = 1;
747
+
748
+ out:
749
+ git_object_free(commit);
750
+ git_status_list_free(status);
751
+
752
+ if (error < 0)
753
+ git_describe_result_free(result);
754
+ else
755
+ *out = result;
756
+
757
+ return error;
758
+ }
759
+
760
+ static int normalize_format_options(
761
+ git_describe_format_options *dst,
762
+ const git_describe_format_options *src)
763
+ {
764
+ if (!src) {
765
+ git_describe_init_format_options(dst, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION);
766
+ return 0;
767
+ }
768
+
769
+ memcpy(dst, src, sizeof(git_describe_format_options));
770
+ return 0;
771
+ }
772
+
773
+ int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *given)
774
+ {
775
+ int error;
776
+ git_repository *repo;
777
+ struct commit_name *name;
778
+ git_describe_format_options opts;
779
+
780
+ assert(out && result);
781
+
782
+ GITERR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options");
783
+ normalize_format_options(&opts, given);
784
+
785
+ git_buf_sanitize(out);
786
+
787
+
788
+ if (opts.always_use_long_format && opts.abbreviated_size == 0) {
789
+ giterr_set(GITERR_DESCRIBE, "Cannot describe - "
790
+ "'always_use_long_format' is incompatible with a zero"
791
+ "'abbreviated_size'");
792
+ return -1;
793
+ }
794
+
795
+
796
+ repo = result->repo;
797
+
798
+ /* If we did find an exact match, then it's the easier method */
799
+ if (result->exact_match) {
800
+ name = result->name;
801
+ if ((error = display_name(out, repo, name)) < 0)
802
+ return error;
803
+
804
+ if (opts.always_use_long_format) {
805
+ const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id;
806
+ if ((error = show_suffix(out, 0, repo, id, opts.abbreviated_size)) < 0)
807
+ return error;
808
+ }
809
+
810
+ if (result->dirty && opts.dirty_suffix)
811
+ git_buf_puts(out, opts.dirty_suffix);
812
+
813
+ return git_buf_oom(out) ? -1 : 0;
814
+ }
815
+
816
+ /* If we didn't find *any* tags, we fall back to the commit's id */
817
+ if (result->fallback_to_id) {
818
+ char hex_oid[GIT_OID_HEXSZ + 1] = {0};
819
+ int size = 0;
820
+
821
+ if ((error = find_unique_abbrev_size(
822
+ &size, repo, &result->commit_id, opts.abbreviated_size)) < 0)
823
+ return -1;
824
+
825
+ git_oid_fmt(hex_oid, &result->commit_id);
826
+ git_buf_put(out, hex_oid, size);
827
+
828
+ if (result->dirty && opts.dirty_suffix)
829
+ git_buf_puts(out, opts.dirty_suffix);
830
+
831
+ return git_buf_oom(out) ? -1 : 0;
832
+ }
833
+
834
+ /* Lastly, if we found a matching tag, we show that */
835
+ name = result->tag->name;
836
+
837
+ if ((error = display_name(out, repo, name)) < 0)
838
+ return error;
839
+
840
+ if (opts.abbreviated_size) {
841
+ if ((error = show_suffix(out, result->tag->depth, repo,
842
+ &result->commit_id, opts.abbreviated_size)) < 0)
843
+ return error;
844
+ }
845
+
846
+ if (result->dirty && opts.dirty_suffix) {
847
+ git_buf_puts(out, opts.dirty_suffix);
848
+ }
849
+
850
+ return git_buf_oom(out) ? -1 : 0;
851
+ }
852
+
853
+ void git_describe_result_free(git_describe_result *result)
854
+ {
855
+ if (result == NULL)
856
+ return;
857
+
858
+ if (result->name) {
859
+ git_tag_free(result->name->tag);
860
+ git__free(result->name->path);
861
+ git__free(result->name);
862
+ }
863
+
864
+ if (result->tag) {
865
+ git_tag_free(result->tag->name->tag);
866
+ git__free(result->tag->name->path);
867
+ git__free(result->tag->name);
868
+ git__free(result->tag);
869
+ }
870
+
871
+ git__free(result);
872
+ }
873
+
874
+ int git_describe_init_options(git_describe_options *opts, unsigned int version)
875
+ {
876
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
877
+ opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT);
878
+ return 0;
879
+ }
880
+
881
+ int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version)
882
+ {
883
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
884
+ opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT);
885
+ return 0;
886
+ }