int commit_interleaver::finish_making_tree_outside_source()

in src/commit_interleaver.h [382:463]


int commit_interleaver::finish_making_tree_outside_source(
    int head_p, sha1_ref base_commit, dir_mask source_dirs,
    bool source_includes_root, const std::vector<sha1_ref> &parents,
    const std::vector<int> &revs, std::vector<git_tree::item_type> &items,
    sha1_ref &tree_sha1) {
  if (parents.size() > max_parents)
    return error(std::to_string(parents.size()) +
                 " is too many parents (max: " + std::to_string(max_parents) +
                 ")");

  if (head_p != -1)
    dirs.active_dirs.bits |= source_dirs.bits;

  // Pick parents for all the other directories.
  std::array<int, dir_mask::max_size> parent_for_d;
  parent_for_d.fill(-1);
  std::bitset<max_parents> contributed;
  std::array<git_tree, max_parents> trees;

  // Index the head first so that its content takes precedence.
  int inactive_p = -1;
  if (head_p != -1)
    if (index_parent_tree_items(
            head_p, head_p, source_dirs, source_includes_root, inactive_p,
            parents[head_p], trees[head_p], parent_for_d, contributed, revs))
      return 1;
  for (int p = 0, pe = parents.size(); p != pe; ++p)
    if (p != head_p)
      if (index_parent_tree_items(head_p, p, source_dirs, source_includes_root,
                                  inactive_p, parents[p], trees[p],
                                  parent_for_d, contributed, revs))
        return 1;

  auto get_dir_p = [&](int d) -> const int & {
    return dirs.active_dirs.test(d) ? parent_for_d[d] : inactive_p;
  };

  // Fill up the items for the tree.
  for (int p = 0, pe = parents.size(); p != pe; ++p) {
    if (!contributed.test(p))
      continue;

    const git_tree &tree = trees[p];
    for (int i = 0; i < tree.num_items; ++i) {
      auto &item = tree.items[i];
      if (source_includes_root && item.type != git_tree::item_type::tree)
        continue;

      int d = dirs.find_dir(item.name);
      assert(d != -1);
      if (!source_dirs.test(d))
        if (p == get_dir_p(d))
          items.push_back(item);
    }
  }

  // Sort and assert that we don't have any duplicates.
  std::sort(items.begin(), items.end(),
            [](const git_tree::item_type &lhs, const git_tree::item_type &rhs) {
              return strcmp(lhs.name, rhs.name) < 0;
            });
  assert(std::adjacent_find(items.begin(), items.end(),
                            [](const git_tree::item_type &lhs,
                               const git_tree::item_type &rhs) {
                              return strcmp(lhs.name, rhs.name) == 0;
                            }) == items.end());

  // Make the tree.
  if (items.size() > dir_mask::max_size)
    return error("too many items (max: " + std::to_string(dir_mask::max_size) +
                 "); constructing tree for " +
                 (base_commit ? base_commit.sha1->to_string()
                              : std::string("merge commit")));
  git_tree tree;
  tree.num_items = items.size();
  tree.items = cache.make_items(items.data(), items.data() + items.size());
  items.clear();
  if (cache.mktree(tree))
    return 1;
  tree_sha1 = tree.sha1;
  return 0;
}