int commit_source::list_first_ancestry_path()

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;
}