static int main_upstream()

in src/split2mono.cpp [261:356]


static int main_upstream(const char *cmd, int argc, const char *argv[]) {
  if (argc != 2)
    return usage("upstream: wrong number of positional arguments", cmd);
  split2monodb main, upstream;
  upstream.is_read_only = true;
  if (main.opendb(argv[0]) || main.parse_upstreams())
    return usage("could not open <dbdir>", cmd);
  if (upstream.opendb(argv[1]) || upstream.parse_upstreams())
    return usage("could not open <dbdir>", cmd);

  // Refuse to self-reference.
  if (main.name == upstream.name)
    return error("refusing to record self as upstream");

  // Linear search for this upstream.
  auto existing_entry = main.upstreams.find(upstream.name);

  // Pretend we've already merged this upstream, but it had no commits or
  // upstreams at the time.
  bool is_new = false;
  if (existing_entry == main.upstreams.end()) {
    is_new = true;
    upstream_entry ue;
    ue.name = upstream.name;
    existing_entry = main.upstreams.emplace(upstream.name, std::move(ue)).first;
  }

  if (existing_entry->second.num_upstreams > (long)upstream.upstreams.size())
    return error("upstream is missing upstreams we already merged");

  if (existing_entry->second.commits_size > upstream.commits_size_on_open())
    return error("upstream is missing commits we already merged");

  if (existing_entry->second.svnbase_size > upstream.svnbase_size_on_open())
    return error("upstream is missing svnbase revs we already merged");

  // Nothing to do if nothing has changed (or the upstream is empty).
  if (existing_entry->second.num_upstreams == (long)upstream.upstreams.size() &&
      existing_entry->second.commits_size == upstream.commits_size_on_open() &&
      existing_entry->second.svnbase_size == upstream.svnbase_size_on_open() &&
      !is_new)
    return 0;

  // Merge the upstream's upstreams (just in memory, for now).
  for (auto ue : upstream.upstreams) {
    if (ue.second.name == main.name)
      return error("upstream: refusing to create upstream-cycle between dbs");
    auto &existing_ue = main.upstreams[ue.second.name];

    // Check that we're only moving forward.
    if (!existing_ue.name.empty())
      if (existing_ue.num_upstreams > ue.second.num_upstreams ||
          existing_ue.commits_size > ue.second.commits_size ||
          existing_ue.svnbase_size > ue.second.svnbase_size)
        return error("upstream's upstream is out-of-date");

    // Update.
    existing_ue = ue.second;
  }

  // Read all missing commits and merge them.
  if (merge_tables<commits_table>(
          main.commits, existing_entry->second.commits_size, upstream.commits,
          upstream.commits_size_on_open()) ||
      merge_tables<svnbase_table>(
          main.svnbase, existing_entry->second.svnbase_size, upstream.svnbase,
          upstream.svnbase_size_on_open()))
    return 1;

  // Close the streams.
  if (main.close_files())
    return error("error closing commits or index after writing");

  // Merge upstreams.
  existing_entry->second.num_upstreams = upstream.upstreams.size();
  existing_entry->second.commits_size = upstream.commits_size_on_open();
  existing_entry->second.svnbase_size = upstream.svnbase_size_on_open();
  int upstreamsfd = openat(main.dbfd, "upstreams", O_WRONLY | O_TRUNC);
  if (upstreamsfd == -1)
    return error("could not reopen upstreams to write merged file");
  FILE *ufile = fdopen(upstreamsfd, "w");
  if (!ufile)
    return error("could not reopen stream for upstreams");
  if (fprintf(ufile, "name: %s\n", main.name.c_str()) < 0)
    return error("could not write repo name");
  for (auto &ue : main.upstreams)
    if (fprintf(ufile,
                "upstream: %s num-upstreams=%ld commits-size=%ld "
                "svnbase-size=%ld\n",
                ue.second.name.c_str(), ue.second.num_upstreams,
                ue.second.commits_size, ue.second.svnbase_size) < 0)
      return error("could not write upstream");
  if (fclose(ufile))
    return error("problem closing new upstream");
  return 0;
}