Merge branch 'ps/pseudo-refs'
Assorted changes around pseudoref handling. * ps/pseudo-refs: bisect: consistently write BISECT_EXPECTED_REV via the refdb refs: complete list of special refs refs: propagate errno when reading special refs fails wt-status: read HEAD and ORIG_HEAD via the refdb
This commit is contained in:
25
bisect.c
25
bisect.c
@ -471,7 +471,6 @@ static int read_bisect_refs(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
|
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
|
||||||
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
|
|
||||||
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
|
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
|
||||||
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
|
static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
|
||||||
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
|
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
|
||||||
@ -707,26 +706,10 @@ static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
|
|||||||
|
|
||||||
static int is_expected_rev(const struct object_id *oid)
|
static int is_expected_rev(const struct object_id *oid)
|
||||||
{
|
{
|
||||||
const char *filename = git_path_bisect_expected_rev();
|
struct object_id expected_oid;
|
||||||
struct stat st;
|
if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
|
||||||
struct strbuf str = STRBUF_INIT;
|
|
||||||
FILE *fp;
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
if (stat(filename, &st) || !S_ISREG(st.st_mode))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
return oideq(oid, &expected_oid);
|
||||||
fp = fopen_or_warn(filename, "r");
|
|
||||||
if (!fp)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (strbuf_getline_lf(&str, fp) != EOF)
|
|
||||||
res = !strcmp(str.buf, oid_to_hex(oid));
|
|
||||||
|
|
||||||
strbuf_release(&str);
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
|
enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
|
||||||
@ -1185,10 +1168,10 @@ int bisect_clean_state(void)
|
|||||||
struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
|
struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
|
||||||
for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
|
for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
|
||||||
string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
|
string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
|
||||||
|
string_list_append(&refs_for_removal, xstrdup("BISECT_EXPECTED_REV"));
|
||||||
result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
|
result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
|
||||||
refs_for_removal.strdup_strings = 1;
|
refs_for_removal.strdup_strings = 1;
|
||||||
string_list_clear(&refs_for_removal, 0);
|
string_list_clear(&refs_for_removal, 0);
|
||||||
unlink_or_warn(git_path_bisect_expected_rev());
|
|
||||||
unlink_or_warn(git_path_bisect_ancestors_ok());
|
unlink_or_warn(git_path_bisect_ancestors_ok());
|
||||||
unlink_or_warn(git_path_bisect_log());
|
unlink_or_warn(git_path_bisect_log());
|
||||||
unlink_or_warn(git_path_bisect_names());
|
unlink_or_warn(git_path_bisect_names());
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
|
|
||||||
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
|
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
|
||||||
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
|
|
||||||
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
|
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
|
||||||
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
|
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
|
||||||
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
|
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
|
||||||
@ -920,7 +919,6 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
|
|||||||
const char *state;
|
const char *state;
|
||||||
int i, verify_expected = 1;
|
int i, verify_expected = 1;
|
||||||
struct object_id oid, expected;
|
struct object_id oid, expected;
|
||||||
struct strbuf buf = STRBUF_INIT;
|
|
||||||
struct oid_array revs = OID_ARRAY_INIT;
|
struct oid_array revs = OID_ARRAY_INIT;
|
||||||
|
|
||||||
if (!argc)
|
if (!argc)
|
||||||
@ -975,10 +973,8 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
|
|||||||
oid_array_append(&revs, &commit->object.oid);
|
oid_array_append(&revs, &commit->object.oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz ||
|
if (read_ref("BISECT_EXPECTED_REV", &expected))
|
||||||
get_oid_hex(buf.buf, &expected) < 0)
|
|
||||||
verify_expected = 0; /* Ignore invalid file contents */
|
verify_expected = 0; /* Ignore invalid file contents */
|
||||||
strbuf_release(&buf);
|
|
||||||
|
|
||||||
for (i = 0; i < revs.nr; i++) {
|
for (i = 0; i < revs.nr; i++) {
|
||||||
if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) {
|
if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) {
|
||||||
@ -987,7 +983,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
|
|||||||
}
|
}
|
||||||
if (verify_expected && !oideq(&revs.oid[i], &expected)) {
|
if (verify_expected && !oideq(&revs.oid[i], &expected)) {
|
||||||
unlink_or_warn(git_path_bisect_ancestors_ok());
|
unlink_or_warn(git_path_bisect_ancestors_ok());
|
||||||
unlink_or_warn(git_path_bisect_expected_rev());
|
delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF);
|
||||||
verify_expected = 0;
|
verify_expected = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
59
refs.c
59
refs.c
@ -1806,8 +1806,10 @@ static int refs_read_special_head(struct ref_store *ref_store,
|
|||||||
int result = -1;
|
int result = -1;
|
||||||
strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname);
|
strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname);
|
||||||
|
|
||||||
if (strbuf_read_file(&content, full_path.buf, 0) < 0)
|
if (strbuf_read_file(&content, full_path.buf, 0) < 0) {
|
||||||
|
*failure_errno = errno;
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
result = parse_loose_ref_contents(content.buf, oid, referent, type,
|
result = parse_loose_ref_contents(content.buf, oid, referent, type,
|
||||||
failure_errno);
|
failure_errno);
|
||||||
@ -1818,15 +1820,66 @@ done:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int is_special_ref(const char *refname)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Special references get written and read directly via the filesystem
|
||||||
|
* by the subsystems that create them. Thus, they must not go through
|
||||||
|
* the reference backend but must instead be read directly. It is
|
||||||
|
* arguable whether this behaviour is sensible, or whether it's simply
|
||||||
|
* a leaky abstraction enabled by us only having a single reference
|
||||||
|
* backend implementation. But at least for a subset of references it
|
||||||
|
* indeed does make sense to treat them specially:
|
||||||
|
*
|
||||||
|
* - FETCH_HEAD may contain multiple object IDs, and each one of them
|
||||||
|
* carries additional metadata like where it came from.
|
||||||
|
*
|
||||||
|
* - MERGE_HEAD may contain multiple object IDs when merging multiple
|
||||||
|
* heads.
|
||||||
|
*
|
||||||
|
* There are some exceptions that you might expect to see on this list
|
||||||
|
* but which are handled exclusively via the reference backend:
|
||||||
|
*
|
||||||
|
* - BISECT_EXPECTED_REV
|
||||||
|
*
|
||||||
|
* - CHERRY_PICK_HEAD
|
||||||
|
*
|
||||||
|
* - HEAD
|
||||||
|
*
|
||||||
|
* - ORIG_HEAD
|
||||||
|
*
|
||||||
|
* - "rebase-apply/" and "rebase-merge/" contain all of the state for
|
||||||
|
* rebases, including some reference-like files. These are
|
||||||
|
* exclusively read and written via the filesystem and never go
|
||||||
|
* through the refdb.
|
||||||
|
*
|
||||||
|
* Writing or deleting references must consistently go either through
|
||||||
|
* the filesystem (special refs) or through the reference backend
|
||||||
|
* (normal ones).
|
||||||
|
*/
|
||||||
|
static const char * const special_refs[] = {
|
||||||
|
"AUTO_MERGE",
|
||||||
|
"FETCH_HEAD",
|
||||||
|
"MERGE_AUTOSTASH",
|
||||||
|
"MERGE_HEAD",
|
||||||
|
};
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(special_refs); i++)
|
||||||
|
if (!strcmp(refname, special_refs[i]))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
|
int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
|
||||||
struct object_id *oid, struct strbuf *referent,
|
struct object_id *oid, struct strbuf *referent,
|
||||||
unsigned int *type, int *failure_errno)
|
unsigned int *type, int *failure_errno)
|
||||||
{
|
{
|
||||||
assert(failure_errno);
|
assert(failure_errno);
|
||||||
if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) {
|
if (is_special_ref(refname))
|
||||||
return refs_read_special_head(ref_store, refname, oid, referent,
|
return refs_read_special_head(ref_store, refname, oid, referent,
|
||||||
type, failure_errno);
|
type, failure_errno);
|
||||||
}
|
|
||||||
|
|
||||||
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
|
return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
|
||||||
type, failure_errno);
|
type, failure_errno);
|
||||||
|
@ -268,4 +268,14 @@ test_expect_success '--exists with directory fails with generic error' '
|
|||||||
test_cmp expect err
|
test_cmp expect err
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '--exists with non-existent special ref' '
|
||||||
|
test_expect_code 2 git show-ref --exists FETCH_HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--exists with existing special ref' '
|
||||||
|
test_when_finished "rm .git/FETCH_HEAD" &&
|
||||||
|
git rev-parse HEAD >.git/FETCH_HEAD &&
|
||||||
|
git show-ref --exists FETCH_HEAD
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -1182,7 +1182,7 @@ test_expect_success 'git bisect reset cleans bisection state properly' '
|
|||||||
git bisect bad $HASH4 &&
|
git bisect bad $HASH4 &&
|
||||||
git bisect reset &&
|
git bisect reset &&
|
||||||
test -z "$(git for-each-ref "refs/bisect/*")" &&
|
test -z "$(git for-each-ref "refs/bisect/*")" &&
|
||||||
test_path_is_missing ".git/BISECT_EXPECTED_REV" &&
|
test_ref_missing BISECT_EXPECTED_REV &&
|
||||||
test_path_is_missing ".git/BISECT_ANCESTORS_OK" &&
|
test_path_is_missing ".git/BISECT_ANCESTORS_OK" &&
|
||||||
test_path_is_missing ".git/BISECT_LOG" &&
|
test_path_is_missing ".git/BISECT_LOG" &&
|
||||||
test_path_is_missing ".git/BISECT_RUN" &&
|
test_path_is_missing ".git/BISECT_RUN" &&
|
||||||
|
22
wt-status.c
22
wt-status.c
@ -1296,26 +1296,32 @@ static char *read_line_from_git_path(const char *filename)
|
|||||||
static int split_commit_in_progress(struct wt_status *s)
|
static int split_commit_in_progress(struct wt_status *s)
|
||||||
{
|
{
|
||||||
int split_in_progress = 0;
|
int split_in_progress = 0;
|
||||||
char *head, *orig_head, *rebase_amend, *rebase_orig_head;
|
struct object_id head_oid, orig_head_oid;
|
||||||
|
char *rebase_amend, *rebase_orig_head;
|
||||||
|
int head_flags, orig_head_flags;
|
||||||
|
|
||||||
if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
|
if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
|
||||||
!s->branch || strcmp(s->branch, "HEAD"))
|
!s->branch || strcmp(s->branch, "HEAD"))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
head = read_line_from_git_path("HEAD");
|
if (read_ref_full("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
|
||||||
orig_head = read_line_from_git_path("ORIG_HEAD");
|
&head_oid, &head_flags) ||
|
||||||
|
read_ref_full("ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
|
||||||
|
&orig_head_oid, &orig_head_flags))
|
||||||
|
return 0;
|
||||||
|
if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
|
||||||
|
return 0;
|
||||||
|
|
||||||
rebase_amend = read_line_from_git_path("rebase-merge/amend");
|
rebase_amend = read_line_from_git_path("rebase-merge/amend");
|
||||||
rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
|
rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
|
||||||
|
|
||||||
if (!head || !orig_head || !rebase_amend || !rebase_orig_head)
|
if (!rebase_amend || !rebase_orig_head)
|
||||||
; /* fall through, no split in progress */
|
; /* fall through, no split in progress */
|
||||||
else if (!strcmp(rebase_amend, rebase_orig_head))
|
else if (!strcmp(rebase_amend, rebase_orig_head))
|
||||||
split_in_progress = !!strcmp(head, rebase_amend);
|
split_in_progress = !!strcmp(oid_to_hex(&head_oid), rebase_amend);
|
||||||
else if (strcmp(orig_head, rebase_orig_head))
|
else if (strcmp(oid_to_hex(&orig_head_oid), rebase_orig_head))
|
||||||
split_in_progress = 1;
|
split_in_progress = 1;
|
||||||
|
|
||||||
free(head);
|
|
||||||
free(orig_head);
|
|
||||||
free(rebase_amend);
|
free(rebase_amend);
|
||||||
free(rebase_orig_head);
|
free(rebase_orig_head);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user