in src/commit_source.h [477:570]
int commit_source::list_first_ancestry_path(git_cache &cache) {
// The path is empty.
if (head == goal) {
num_fparents_from_start = 0;
return 0;
}
assert(!extra_commits_have_been_translated);
std::string start = goal->to_string();
std::string stop = head->to_string();
auto &git_reply = cache.git_reply;
const char *argv[] = {
"git",
"log",
"--format=tformat:%H %ct %P",
"--ancestry-path",
start.c_str(),
"--not",
stop.c_str(),
nullptr,
};
git_reply.clear();
if (call_git(argv, nullptr, "", git_reply))
return 1;
git_reply.push_back(0);
struct ancestry_node {
sha1_ref commit;
long long ct = -1;
const char *parents = nullptr;
};
std::vector<ancestry_node> ancestry;
auto in_ancestry = std::make_unique<sha1_trie<git_cache::sha1_single>>();
bool was_inserted;
const char *current = git_reply.data();
const char *end = git_reply.data() + git_reply.size() - 1;
while (current != end) {
ancestry.emplace_back();
if (cache.pool.parse_sha1(current, ancestry.back().commit) ||
parse_space(current) || parse_ct(current, ancestry.back().ct) ||
parse_space(current))
return 1;
in_ancestry->insert(*ancestry.back().commit, was_inserted);
ancestry.back().parents = current;
if (parse_through_newline(current))
return 1;
}
auto included = std::make_unique<sha1_trie<git_cache::sha1_single>>();
in_ancestry->insert(*head, was_inserted);
included->insert(*goal, was_inserted);
for (auto &an : ancestry) {
if (!included->lookup(*an.commit))
continue;
// We don't care much about the parents, we're just storing the metadata.
fparents.emplace_back(source_index);
fparents.back().commit = an.commit;
fparents.back().ct = an.ct;
fparents.back().has_parents = true;
validate_last_ct();
// Should always have at least one parent.
const char *current = an.parents;
int p = 0;
auto handle_parent = [&]() {
if (p == 1)
fparents.back().is_merge = true;
sha1_ref parent;
if (cache.pool.parse_sha1(current, parent))
return 1;
if (fparents.back().head_p != -1)
return 0;
if (!in_ancestry->lookup(*parent))
return 0;
fparents.back().head_p = p;
included->insert(*parent, was_inserted);
return 0;
};
if (handle_parent())
return error("failed to parse first parent in ancestry path");
for (++p; !parse_space(current); ++p)
if (handle_parent())
return error("failed to parse parent in ancestry path");
if (*current != '\n')
return error("failed to parse parents in ancestry path");
if (fparents.back().head_p == -1)
return error("failed to traverse ancestry path");
}
num_fparents_from_start = fparents.size();
return 0;
}