THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

220 lines
5.5 KiB

  1. #include "cache.h"
  2. #include "refs.h"
  3. #include "strbuf.h"
  4. #include "worktree.h"
  5. void free_worktrees(struct worktree **worktrees)
  6. {
  7. int i = 0;
  8. for (i = 0; worktrees[i]; i++) {
  9. free(worktrees[i]->path);
  10. free(worktrees[i]->git_dir);
  11. free(worktrees[i]->head_ref);
  12. free(worktrees[i]);
  13. }
  14. free (worktrees);
  15. }
  16. /*
  17. * read 'path_to_ref' into 'ref'. Also if is_detached is not NULL,
  18. * set is_detached to 1 (0) if the ref is detached (is not detached).
  19. *
  20. * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
  21. * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
  22. * git_path). Parse the ref ourselves.
  23. *
  24. * return -1 if the ref is not a proper ref, 0 otherwise (success)
  25. */
  26. static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
  27. {
  28. if (is_detached)
  29. *is_detached = 0;
  30. if (!strbuf_readlink(ref, path_to_ref, 0)) {
  31. /* HEAD is symbolic link */
  32. if (!starts_with(ref->buf, "refs/") ||
  33. check_refname_format(ref->buf, 0))
  34. return -1;
  35. } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {
  36. /* textual symref or detached */
  37. if (!starts_with(ref->buf, "ref:")) {
  38. if (is_detached)
  39. *is_detached = 1;
  40. } else {
  41. strbuf_remove(ref, 0, strlen("ref:"));
  42. strbuf_trim(ref);
  43. if (check_refname_format(ref->buf, 0))
  44. return -1;
  45. }
  46. } else
  47. return -1;
  48. return 0;
  49. }
  50. /**
  51. * Add the head_sha1 and head_ref (if not detached) to the given worktree
  52. */
  53. static void add_head_info(struct strbuf *head_ref, struct worktree *worktree)
  54. {
  55. if (head_ref->len) {
  56. if (worktree->is_detached) {
  57. get_sha1_hex(head_ref->buf, worktree->head_sha1);
  58. } else {
  59. resolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL);
  60. worktree->head_ref = strbuf_detach(head_ref, NULL);
  61. }
  62. }
  63. }
  64. /**
  65. * get the main worktree
  66. */
  67. static struct worktree *get_main_worktree(void)
  68. {
  69. struct worktree *worktree = NULL;
  70. struct strbuf path = STRBUF_INIT;
  71. struct strbuf worktree_path = STRBUF_INIT;
  72. struct strbuf gitdir = STRBUF_INIT;
  73. struct strbuf head_ref = STRBUF_INIT;
  74. int is_bare = 0;
  75. int is_detached = 0;
  76. strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
  77. strbuf_addbuf(&worktree_path, &gitdir);
  78. is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
  79. if (is_bare)
  80. strbuf_strip_suffix(&worktree_path, "/.");
  81. strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
  82. if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
  83. goto done;
  84. worktree = xmalloc(sizeof(struct worktree));
  85. worktree->path = strbuf_detach(&worktree_path, NULL);
  86. worktree->git_dir = strbuf_detach(&gitdir, NULL);
  87. worktree->is_bare = is_bare;
  88. worktree->head_ref = NULL;
  89. worktree->is_detached = is_detached;
  90. add_head_info(&head_ref, worktree);
  91. done:
  92. strbuf_release(&path);
  93. strbuf_release(&gitdir);
  94. strbuf_release(&worktree_path);
  95. strbuf_release(&head_ref);
  96. return worktree;
  97. }
  98. static struct worktree *get_linked_worktree(const char *id)
  99. {
  100. struct worktree *worktree = NULL;
  101. struct strbuf path = STRBUF_INIT;
  102. struct strbuf worktree_path = STRBUF_INIT;
  103. struct strbuf gitdir = STRBUF_INIT;
  104. struct strbuf head_ref = STRBUF_INIT;
  105. int is_detached = 0;
  106. if (!id)
  107. die("Missing linked worktree name");
  108. strbuf_addf(&gitdir, "%s/worktrees/%s",
  109. absolute_path(get_git_common_dir()), id);
  110. strbuf_addf(&path, "%s/gitdir", gitdir.buf);
  111. if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
  112. /* invalid gitdir file */
  113. goto done;
  114. strbuf_rtrim(&worktree_path);
  115. if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
  116. strbuf_reset(&worktree_path);
  117. strbuf_addstr(&worktree_path, absolute_path("."));
  118. strbuf_strip_suffix(&worktree_path, "/.");
  119. }
  120. strbuf_reset(&path);
  121. strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
  122. if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
  123. goto done;
  124. worktree = xmalloc(sizeof(struct worktree));
  125. worktree->path = strbuf_detach(&worktree_path, NULL);
  126. worktree->git_dir = strbuf_detach(&gitdir, NULL);
  127. worktree->is_bare = 0;
  128. worktree->head_ref = NULL;
  129. worktree->is_detached = is_detached;
  130. add_head_info(&head_ref, worktree);
  131. done:
  132. strbuf_release(&path);
  133. strbuf_release(&gitdir);
  134. strbuf_release(&worktree_path);
  135. strbuf_release(&head_ref);
  136. return worktree;
  137. }
  138. struct worktree **get_worktrees(void)
  139. {
  140. struct worktree **list = NULL;
  141. struct strbuf path = STRBUF_INIT;
  142. DIR *dir;
  143. struct dirent *d;
  144. int counter = 0, alloc = 2;
  145. list = xmalloc(alloc * sizeof(struct worktree *));
  146. if ((list[counter] = get_main_worktree()))
  147. counter++;
  148. strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
  149. dir = opendir(path.buf);
  150. strbuf_release(&path);
  151. if (dir) {
  152. while ((d = readdir(dir)) != NULL) {
  153. struct worktree *linked = NULL;
  154. if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
  155. continue;
  156. if ((linked = get_linked_worktree(d->d_name))) {
  157. ALLOC_GROW(list, counter + 1, alloc);
  158. list[counter++] = linked;
  159. }
  160. }
  161. closedir(dir);
  162. }
  163. ALLOC_GROW(list, counter + 1, alloc);
  164. list[counter] = NULL;
  165. return list;
  166. }
  167. char *find_shared_symref(const char *symref, const char *target)
  168. {
  169. char *existing = NULL;
  170. struct strbuf path = STRBUF_INIT;
  171. struct strbuf sb = STRBUF_INIT;
  172. struct worktree **worktrees = get_worktrees();
  173. int i = 0;
  174. for (i = 0; worktrees[i]; i++) {
  175. strbuf_reset(&path);
  176. strbuf_reset(&sb);
  177. strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
  178. if (parse_ref(path.buf, &sb, NULL)) {
  179. continue;
  180. }
  181. if (!strcmp(sb.buf, target)) {
  182. existing = xstrdup(worktrees[i]->path);
  183. break;
  184. }
  185. }
  186. strbuf_release(&path);
  187. strbuf_release(&sb);
  188. free_worktrees(worktrees);
  189. return existing;
  190. }