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