Fareanor
Fareanor

Reputation: 6805

"cannot set OID on symbolic reference" when trying to fast-forward with libgit2

Note: I tagged C and C++ since libgit2 is a C library and I'm writing a C++ wrapper for common git commands. We'll see here both C and C++ code and as far as I am concerned it does not matter if I get a C or C++ oriented solution (I'll adapt it myself if needed).


I'm trying to perform a fast-forward update of a repository in a gitPull() function when the branch is fast-forwardable (using libgit2). I want to perform a merge only when necessary.

My strategy looks as follows (I removed the error-checking, ... in order to target only the essentials and to make the code as simple as possible):

// INFO: I assume here I already have an open repository in a "repo" variable.

// Do the fetch --> works fine
// ...

// Compare HEAD and FETCH_HEAD
git_oid head_id;
git_reference_name_to_id(&head_id, repo, "HEAD");
git_oid fetch_head_id;
git_reference_name_to_id(&fetch_head_id, repo, "FETCH_HEAD");

if(!git_oid_equal(&fetch_head_id, &head_id)) // A pull is needed
{
    // Check if we can fast-forward or need a merge
    git_annotated_commit * fetch_head_annotated_commit = nullptr;
    git_annotated_commit_lookup(&fetch_head_annotated_commit, repo, &fetch_head_id);

    git_merge_analysis_t an_out;
    git_merge_preference_t pref_out;
    git_merge_analysis(&an_out, &pref_out, repo, const_cast<const git_annotated_commit **>(&fetch_head_annotated_commit),1);

    if(an_out & GIT_MERGE_ANALYSIS_FASTFORWARD) // A fast-forward is possible
    {
        git_reference * head_ref = nullptr;
        git_reference_lookup(&head_ref, repo, "HEAD");

        git_reference * new_head_ref = nullptr;
        git_reference_set_target(&new_head_ref, head_ref, &fetch_head_id, "");

        git_checkout_head(repo, nullptr);

        git_reference_free(new_head_ref);
        git_reference_free(head_ref);
    }
    else // A merge is needed
    {
        // Do the merge and create the related commit
        // ...
    }
    git_annotated_commit_free(fetch_head_annotated_commit);
}

What is strange is that the condition if(an_out & GIT_MERGE_ANALYSIS_FASTFORWARD) is true but when I run the function git_reference_set_target(&new_head_ref, head_ref, &fetch_head_id, "");, I get the following error:

"cannot set OID on symbolic reference"


I have made a research and I found in the source code of libgit2 in refs.c what you can see below:

static int ensure_is_an_updatable_direct_reference(git_reference *ref)
{
    if (ref->type == GIT_REFERENCE_DIRECT)
        return 0;

    git_error_set(GIT_ERROR_REFERENCE, "cannot set OID on symbolic reference");
    return -1;
}

int git_reference_set_target(
    git_reference **out,
    git_reference *ref,
    const git_oid *id,
    const char *log_message)
{
    int error;
    git_repository *repo;

    assert(out && ref && id);

    repo = ref->db->repo;

    if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
        return error;

    return git_reference_create_matching(out, repo, ref->name, id, 1, &ref->target.oid, log_message);
}

By reading this code, we can see that the issue is that the ref parameter which is the "HEAD" reference in my case, is not an "updatable direct reference".
In other words, there is no direct path found from "HEAD" to "FETCH_HEAD" (as I passed the fetch_head_id).

What I don't understand is that, if I am correct, the way to check if a branch can be fast-forwarded is to verify if it exists a direct path between "HEAD" and "FETCH_HEAD".

So it seems contradictory in my mind to enter the condition and hear it said that it is not an "updatable direct reference".


My question is: What did I miss ? What did I misunderstand ? (quite wide question I know, this is why I provided as many details as possible).

I will be very grateful for any tips that will help me to get rid of my issue and make it work.

Upvotes: 1

Views: 190

Answers (1)

Fareanor
Fareanor

Reputation: 6805

This morning I just realized i'm quite stupid :)

"HEAD" is a symbolic reference to "refs/heads/master" in my case. So what the error notifies is that I need to pass the direct reference, not an alias.

Upvotes: 1

Related Questions