PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/tig-1.0/refs.c

#
C | 242 lines | 183 code | 39 blank | 20 comment | 51 complexity | d51ef25686e311ed9692d8310a9c039d MD5 | raw file
Possible License(s): GPL-2.0
  1. /* Copyright (c) 2006-2012 Jonas Fonseca <fonseca@diku.dk>
  2. *
  3. * This program is free software; you can redistribute it and/or
  4. * modify it under the terms of the GNU General Public License as
  5. * published by the Free Software Foundation; either version 2 of
  6. * the License, or (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #include "tig.h"
  14. #include "io.h"
  15. #include "refs.h"
  16. static struct ref **refs = NULL;
  17. static size_t refs_size = 0;
  18. static struct ref *refs_head = NULL;
  19. static struct ref_list **ref_lists = NULL;
  20. static size_t ref_lists_size = 0;
  21. DEFINE_ALLOCATOR(realloc_refs, struct ref *, 256)
  22. DEFINE_ALLOCATOR(realloc_refs_list, struct ref *, 8)
  23. DEFINE_ALLOCATOR(realloc_ref_lists, struct ref_list *, 8)
  24. static int
  25. compare_refs(const void *ref1_, const void *ref2_)
  26. {
  27. const struct ref *ref1 = *(const struct ref **)ref1_;
  28. const struct ref *ref2 = *(const struct ref **)ref2_;
  29. if (ref1->tag != ref2->tag)
  30. return ref2->tag - ref1->tag;
  31. if (ref1->ltag != ref2->ltag)
  32. return ref2->ltag - ref1->ltag;
  33. if (ref1->head != ref2->head)
  34. return ref2->head - ref1->head;
  35. if (ref1->tracked != ref2->tracked)
  36. return ref2->tracked - ref1->tracked;
  37. if (ref1->replace != ref2->replace)
  38. return ref2->replace - ref1->replace;
  39. /* Order remotes last. */
  40. if (ref1->remote != ref2->remote)
  41. return ref1->remote - ref2->remote;
  42. return strcmp(ref1->name, ref2->name);
  43. }
  44. void
  45. foreach_ref(bool (*visitor)(void *data, const struct ref *ref), void *data)
  46. {
  47. size_t i;
  48. for (i = 0; i < refs_size; i++)
  49. if (!visitor(data, refs[i]))
  50. break;
  51. }
  52. struct ref *
  53. get_ref_head()
  54. {
  55. return refs_head;
  56. }
  57. struct ref_list *
  58. get_ref_list(const char *id)
  59. {
  60. struct ref_list *list;
  61. size_t i;
  62. for (i = 0; i < ref_lists_size; i++)
  63. if (!strcmp(id, ref_lists[i]->id))
  64. return ref_lists[i];
  65. if (!realloc_ref_lists(&ref_lists, ref_lists_size, 1))
  66. return NULL;
  67. list = calloc(1, sizeof(*list));
  68. if (!list)
  69. return NULL;
  70. for (i = 0; i < refs_size; i++) {
  71. if (!strcmp(id, refs[i]->id) &&
  72. realloc_refs_list(&list->refs, list->size, 1))
  73. list->refs[list->size++] = refs[i];
  74. }
  75. if (!list->refs) {
  76. free(list);
  77. return NULL;
  78. }
  79. qsort(list->refs, list->size, sizeof(*list->refs), compare_refs);
  80. ref_lists[ref_lists_size++] = list;
  81. return list;
  82. }
  83. struct ref_opt {
  84. const char *remote;
  85. const char *head;
  86. };
  87. static int
  88. read_ref(char *id, size_t idlen, char *name, size_t namelen, void *data)
  89. {
  90. struct ref_opt *opt = data;
  91. struct ref *ref = NULL;
  92. bool tag = FALSE;
  93. bool ltag = FALSE;
  94. bool remote = FALSE;
  95. bool replace = FALSE;
  96. bool tracked = FALSE;
  97. bool head = FALSE;
  98. int pos;
  99. if (!prefixcmp(name, "refs/tags/")) {
  100. if (!suffixcmp(name, namelen, "^{}")) {
  101. namelen -= 3;
  102. name[namelen] = 0;
  103. } else {
  104. ltag = TRUE;
  105. }
  106. tag = TRUE;
  107. namelen -= STRING_SIZE("refs/tags/");
  108. name += STRING_SIZE("refs/tags/");
  109. } else if (!prefixcmp(name, "refs/remotes/")) {
  110. remote = TRUE;
  111. namelen -= STRING_SIZE("refs/remotes/");
  112. name += STRING_SIZE("refs/remotes/");
  113. tracked = !strcmp(opt->remote, name);
  114. } else if (!prefixcmp(name, "refs/replace/")) {
  115. replace = TRUE;
  116. id = name + strlen("refs/replace/");
  117. idlen = namelen - strlen("refs/replace/");
  118. name = "replaced";
  119. namelen = strlen(name);
  120. } else if (!prefixcmp(name, "refs/heads/")) {
  121. namelen -= STRING_SIZE("refs/heads/");
  122. name += STRING_SIZE("refs/heads/");
  123. head = strlen(opt->head) == namelen
  124. && !strncmp(opt->head, name, namelen);
  125. } else if (!strcmp(name, "HEAD")) {
  126. /* Handle the case of HEAD not being a symbolic ref,
  127. * i.e. during a rebase. */
  128. if (*opt->head)
  129. return OK;
  130. head = TRUE;
  131. }
  132. /* If we are reloading or it's an annotated tag, replace the
  133. * previous SHA1 with the resolved commit id; relies on the fact
  134. * git-ls-remote lists the commit id of an annotated tag right
  135. * before the commit id it points to. */
  136. for (pos = 0; pos < refs_size; pos++) {
  137. int cmp = replace ? strcmp(id, refs[pos]->id) : strcmp(name, refs[pos]->name);
  138. if (!cmp) {
  139. ref = refs[pos];
  140. break;
  141. }
  142. }
  143. if (!ref) {
  144. if (!realloc_refs(&refs, refs_size, 1))
  145. return ERR;
  146. ref = calloc(1, sizeof(*ref) + namelen);
  147. if (!ref)
  148. return ERR;
  149. refs[refs_size++] = ref;
  150. strncpy(ref->name, name, namelen);
  151. }
  152. ref->head = head;
  153. ref->tag = tag;
  154. ref->ltag = ltag;
  155. ref->remote = remote;
  156. ref->replace = replace;
  157. ref->tracked = tracked;
  158. string_ncopy_do(ref->id, SIZEOF_REV, id, idlen);
  159. if (head)
  160. refs_head = ref;
  161. return OK;
  162. }
  163. int
  164. reload_refs(const char *git_dir, const char *remote_name, char *head, size_t headlen)
  165. {
  166. const char *head_argv[] = {
  167. "git", "symbolic-ref", "HEAD", NULL
  168. };
  169. const char *ls_remote_argv[SIZEOF_ARG] = {
  170. "git", "ls-remote", git_dir, NULL
  171. };
  172. static bool init = FALSE;
  173. struct ref_opt opt = { remote_name, head };
  174. size_t i;
  175. if (!init) {
  176. if (!argv_from_env(ls_remote_argv, "TIG_LS_REMOTE"))
  177. return ERR;
  178. init = TRUE;
  179. }
  180. if (!*git_dir)
  181. return OK;
  182. if (io_run_buf(head_argv, head, headlen) &&
  183. !prefixcmp(head, "refs/heads/")) {
  184. char *offset = head + STRING_SIZE("refs/heads/");
  185. memmove(head, offset, strlen(offset) + 1);
  186. }
  187. refs_head = NULL;
  188. for (i = 0; i < refs_size; i++)
  189. refs[i]->id[0] = 0;
  190. if (io_run_load(ls_remote_argv, "\t", read_ref, &opt) == ERR)
  191. return ERR;
  192. /* Update the ref lists to reflect changes. */
  193. for (i = 0; i < ref_lists_size; i++) {
  194. struct ref_list *list = ref_lists[i];
  195. size_t old, new;
  196. for (old = new = 0; old < list->size; old++)
  197. if (!strcmp(list->id, list->refs[old]->id))
  198. list->refs[new++] = list->refs[old];
  199. list->size = new;
  200. }
  201. qsort(refs, refs_size, sizeof(*refs), compare_refs);
  202. return OK;
  203. }