Skip to content

Commit c5d41d4

Browse files
authored
Merge pull request libgit2#5563 from pks-t/pks/worktree-heads
Access HEAD via the refdb backends
2 parents 52ccbc5 + ce4cb07 commit c5d41d4

File tree

8 files changed

+119
-245
lines changed

8 files changed

+119
-245
lines changed

src/branch.c

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -134,39 +134,37 @@ int git_branch_create_from_annotated(
134134
repository, branch_name, commit->commit, commit->description, force);
135135
}
136136

137-
static int branch_equals(git_repository *repo, const char *path, void *payload)
137+
static int branch_is_checked_out(git_repository *worktree, void *payload)
138138
{
139139
git_reference *branch = (git_reference *) payload;
140140
git_reference *head = NULL;
141-
int equal = 0;
141+
int error;
142142

143-
if (git_reference__read_head(&head, repo, path) < 0 ||
144-
git_reference_type(head) != GIT_REFERENCE_SYMBOLIC)
145-
goto done;
143+
if (git_repository_is_bare(worktree))
144+
return 0;
146145

147-
equal = !git__strcmp(head->target.symbolic, branch->name);
146+
if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0) {
147+
if (error == GIT_ENOTFOUND)
148+
error = 0;
149+
goto out;
150+
}
148151

149-
done:
152+
if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC)
153+
goto out;
154+
155+
error = !git__strcmp(head->target.symbolic, branch->name);
156+
157+
out:
150158
git_reference_free(head);
151-
return equal;
159+
return error;
152160
}
153161

154162
int git_branch_is_checked_out(const git_reference *branch)
155163
{
156-
git_repository *repo;
157-
int flags = 0;
158-
159-
assert(branch);
160-
161164
if (!git_reference_is_branch(branch))
162165
return 0;
163-
164-
repo = git_reference_owner(branch);
165-
166-
if (git_repository_is_bare(repo))
167-
flags |= GIT_REPOSITORY_FOREACH_HEAD_SKIP_REPO;
168-
169-
return git_repository_foreach_head(repo, branch_equals, flags, (void *) branch) == 1;
166+
return git_repository_foreach_worktree(git_reference_owner(branch),
167+
branch_is_checked_out, (void *)branch) == 1;
170168
}
171169

172170
int git_branch_delete(git_reference *branch)

src/refs.c

Lines changed: 25 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -237,40 +237,6 @@ int git_reference_lookup_resolved(
237237
return 0;
238238
}
239239

240-
int git_reference__read_head(
241-
git_reference **out,
242-
git_repository *repo,
243-
const char *path)
244-
{
245-
git_buf reference = GIT_BUF_INIT;
246-
char *name = NULL;
247-
int error;
248-
249-
if ((error = git_futils_readbuffer(&reference, path)) < 0)
250-
goto out;
251-
git_buf_rtrim(&reference);
252-
253-
if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) == 0) {
254-
git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF));
255-
256-
name = git_path_basename(path);
257-
258-
if ((*out = git_reference__alloc_symbolic(name, reference.ptr)) == NULL) {
259-
error = -1;
260-
goto out;
261-
}
262-
} else {
263-
if ((error = git_reference_lookup(out, repo, reference.ptr)) < 0)
264-
goto out;
265-
}
266-
267-
out:
268-
git__free(name);
269-
git_buf_dispose(&reference);
270-
271-
return error;
272-
}
273-
274240
int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
275241
{
276242
int error = 0, i;
@@ -605,102 +571,62 @@ int git_reference_symbolic_set_target(
605571
typedef struct {
606572
const char *old_name;
607573
git_refname_t new_name;
608-
} rename_cb_data;
574+
} refs_update_head_payload;
609575

610-
static int update_wt_heads(git_repository *repo, const char *path, void *payload)
576+
static int refs_update_head(git_repository *worktree, void *_payload)
611577
{
612-
rename_cb_data *data = (rename_cb_data *) payload;
613-
git_reference *head = NULL;
614-
char *gitdir = NULL;
578+
refs_update_head_payload *payload = (refs_update_head_payload *)_payload;
579+
git_reference *head = NULL, *updated = NULL;
615580
int error;
616581

617-
if ((error = git_reference__read_head(&head, repo, path)) < 0) {
618-
git_error_set(GIT_ERROR_REFERENCE, "could not read HEAD when renaming references");
619-
goto out;
620-
}
621-
622-
if ((gitdir = git_path_dirname(path)) == NULL) {
623-
error = -1;
582+
if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0)
624583
goto out;
625-
}
626584

627585
if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC ||
628-
git__strcmp(head->target.symbolic, data->old_name) != 0) {
629-
error = 0;
586+
git__strcmp(git_reference_symbolic_target(head), payload->old_name) != 0)
630587
goto out;
631-
}
632588

633-
/* Update HEAD it was pointing to the reference being renamed */
634-
if ((error = git_repository_create_head(gitdir, data->new_name)) < 0) {
589+
/* Update HEAD if it was pointing to the reference being renamed */
590+
if ((error = git_reference_symbolic_set_target(&updated, head, payload->new_name, NULL)) < 0) {
635591
git_error_set(GIT_ERROR_REFERENCE, "failed to update HEAD after renaming reference");
636592
goto out;
637593
}
638594

639595
out:
596+
git_reference_free(updated);
640597
git_reference_free(head);
641-
git__free(gitdir);
642-
643-
return error;
644-
}
645-
646-
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
647-
const git_signature *signature, const char *message)
648-
{
649-
git_repository *repo;
650-
git_refname_t normalized;
651-
bool should_head_be_updated = false;
652-
int error = 0;
653-
654-
assert(ref && new_name && signature);
655-
656-
repo = git_reference_owner(ref);
657-
658-
if ((error = reference_normalize_for_repo(
659-
normalized, repo, new_name, true)) < 0)
660-
return error;
661-
662-
/* Check if we have to update HEAD. */
663-
if ((error = git_branch_is_head(ref)) < 0)
664-
return error;
665-
666-
should_head_be_updated = (error > 0);
667-
668-
if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
669-
return error;
670-
671-
/* Update HEAD if it was pointing to the reference being renamed */
672-
if (should_head_be_updated) {
673-
error = git_repository_set_head(ref->db->repo, normalized);
674-
} else {
675-
rename_cb_data payload;
676-
payload.old_name = ref->name;
677-
memcpy(&payload.new_name, &normalized, sizeof(normalized));
678-
679-
error = git_repository_foreach_head(repo, update_wt_heads, 0, &payload);
680-
}
681-
682598
return error;
683599
}
684600

685-
686601
int git_reference_rename(
687602
git_reference **out,
688603
git_reference *ref,
689604
const char *new_name,
690605
int force,
691606
const char *log_message)
692607
{
693-
git_signature *who;
608+
refs_update_head_payload payload;
609+
git_signature *signature;
610+
git_repository *repo;
694611
int error;
695612

696613
assert(out && ref);
697614

698-
if ((error = git_reference__log_signature(&who, ref->db->repo)) < 0)
699-
return error;
615+
repo = git_reference_owner(ref);
700616

701-
error = reference__rename(out, ref, new_name, force, who, log_message);
702-
git_signature_free(who);
617+
if ((error = git_reference__log_signature(&signature, repo)) < 0 ||
618+
(error = reference_normalize_for_repo(payload.new_name, repo, new_name, true)) < 0 ||
619+
(error = git_refdb_rename(out, ref->db, ref->name, payload.new_name, force, signature, log_message)) < 0)
620+
goto out;
621+
622+
payload.old_name = ref->name;
703623

624+
/* We may have to update any HEAD that was pointing to the renamed reference. */
625+
if ((error = git_repository_foreach_worktree(repo, refs_update_head, &payload)) < 0)
626+
goto out;
627+
628+
out:
629+
git_signature_free(signature);
704630
return error;
705631
}
706632

src/refs.h

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -116,24 +116,6 @@ int git_reference_lookup_resolved(
116116
const char *name,
117117
int max_deref);
118118

119-
/**
120-
* Read reference from a file.
121-
*
122-
* This function will read in the file at `path`. If it is a
123-
* symref, it will return a new unresolved symbolic reference
124-
* with the given name pointing to the reference pointed to by
125-
* the file. If it is not a symbolic reference, it will return
126-
* the resolved reference.
127-
*
128-
* Note that because the refdb is not involved for symbolic references, they
129-
* won't be owned, hence you should either not make the returned reference
130-
* 'externally visible', or perform the lookup before returning it to the user.
131-
*/
132-
int git_reference__read_head(
133-
git_reference **out,
134-
git_repository *repo,
135-
const char *path);
136-
137119
int git_reference__log_signature(git_signature **out, git_repository *repo);
138120

139121
/** Update a reference after a commit. */

src/repository.c

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,12 +2177,6 @@ int git_repository_head_detached(git_repository *repo)
21772177
return exists;
21782178
}
21792179

2180-
static int get_worktree_file_path(git_buf *out, git_repository *repo, const char *worktree, const char *file)
2181-
{
2182-
git_buf_clear(out);
2183-
return git_buf_printf(out, "%s/worktrees/%s/%s", repo->commondir, worktree, file);
2184-
}
2185-
21862180
int git_repository_head_detached_for_worktree(git_repository *repo, const char *name)
21872181
{
21882182
git_reference *ref = NULL;
@@ -2223,73 +2217,77 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
22232217

22242218
int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name)
22252219
{
2226-
git_buf path = GIT_BUF_INIT;
2220+
git_repository *worktree_repo = NULL;
2221+
git_worktree *worktree = NULL;
22272222
git_reference *head = NULL;
22282223
int error;
22292224

22302225
assert(out && repo && name);
22312226

22322227
*out = NULL;
22332228

2234-
if ((error = get_worktree_file_path(&path, repo, name, GIT_HEAD_FILE)) < 0 ||
2235-
(error = git_reference__read_head(&head, repo, path.ptr)) < 0)
2229+
if ((error = git_worktree_lookup(&worktree, repo, name)) < 0 ||
2230+
(error = git_repository_open_from_worktree(&worktree_repo, worktree)) < 0 ||
2231+
(error = git_reference_lookup(&head, worktree_repo, GIT_HEAD_FILE)) < 0)
22362232
goto out;
22372233

22382234
if (git_reference_type(head) != GIT_REFERENCE_DIRECT) {
2239-
git_reference *resolved;
2240-
2241-
error = git_reference_lookup_resolved(&resolved, repo, git_reference_symbolic_target(head), -1);
2242-
git_reference_free(head);
2243-
head = resolved;
2235+
if ((error = git_reference_lookup_resolved(out, worktree_repo, git_reference_symbolic_target(head), -1)) < 0)
2236+
goto out;
2237+
} else {
2238+
*out = head;
2239+
head = NULL;
22442240
}
22452241

2246-
*out = head;
2247-
22482242
out:
2249-
if (error)
2250-
git_reference_free(head);
2251-
2252-
git_buf_dispose(&path);
2253-
2243+
git_reference_free(head);
2244+
git_worktree_free(worktree);
2245+
git_repository_free(worktree_repo);
22542246
return error;
22552247
}
22562248

2257-
int git_repository_foreach_head(git_repository *repo,
2258-
git_repository_foreach_head_cb cb,
2259-
int flags, void *payload)
2249+
int git_repository_foreach_worktree(git_repository *repo,
2250+
git_repository_foreach_worktree_cb cb,
2251+
void *payload)
22602252
{
2261-
git_strarray worktrees = GIT_VECTOR_INIT;
2262-
git_buf path = GIT_BUF_INIT;
2263-
int error = 0;
2253+
git_strarray worktrees = {0};
2254+
git_repository *worktree_repo = NULL;
2255+
git_worktree *worktree = NULL;
2256+
int error;
22642257
size_t i;
22652258

2259+
if ((error = git_repository_open(&worktree_repo, repo->commondir)) < 0 ||
2260+
(error = cb(worktree_repo, payload) != 0))
2261+
goto out;
22662262

2267-
if (!(flags & GIT_REPOSITORY_FOREACH_HEAD_SKIP_REPO)) {
2268-
/* Gather HEAD of main repository */
2269-
if ((error = git_buf_joinpath(&path, repo->commondir, GIT_HEAD_FILE)) < 0 ||
2270-
(error = cb(repo, path.ptr, payload) != 0))
2271-
goto out;
2272-
}
2263+
git_repository_free(worktree_repo);
2264+
worktree_repo = NULL;
22732265

2274-
if (!(flags & GIT_REPOSITORY_FOREACH_HEAD_SKIP_WORKTREES)) {
2275-
if ((error = git_worktree_list(&worktrees, repo)) < 0) {
2276-
error = 0;
2277-
goto out;
2278-
}
2266+
if ((error = git_worktree_list(&worktrees, repo)) < 0)
2267+
goto out;
22792268

2280-
/* Gather HEADs of all worktrees */
2281-
for (i = 0; i < worktrees.count; i++) {
2282-
if (get_worktree_file_path(&path, repo, worktrees.strings[i], GIT_HEAD_FILE) < 0)
2283-
continue;
2269+
for (i = 0; i < worktrees.count; i++) {
2270+
git_repository_free(worktree_repo);
2271+
worktree_repo = NULL;
2272+
git_worktree_free(worktree);
2273+
worktree = NULL;
22842274

2285-
if ((error = cb(repo, path.ptr, payload)) != 0)
2275+
if ((error = git_worktree_lookup(&worktree, repo, worktrees.strings[i]) < 0) ||
2276+
(error = git_repository_open_from_worktree(&worktree_repo, worktree)) < 0) {
2277+
if (error != GIT_ENOTFOUND)
22862278
goto out;
2279+
error = 0;
2280+
continue;
22872281
}
2282+
2283+
if ((error = cb(worktree_repo, payload)) != 0)
2284+
goto out;
22882285
}
22892286

22902287
out:
2291-
git_buf_dispose(&path);
22922288
git_strarray_dispose(&worktrees);
2289+
git_repository_free(worktree_repo);
2290+
git_worktree_free(worktree);
22932291
return error;
22942292
}
22952293

0 commit comments

Comments
 (0)