in src/split2mono.cpp [403:560]
static int main_interleave_commits(const char *cmd, int argc,
const char *argv[]) {
if (argc < 1)
return usage("interleave-commits: missing <dbdir>", cmd);
split2monodb db;
if (db.opendb(argv[0]))
return usage("could not open <dbdir>", cmd);
--argc, ++argv;
// Copied from svn2git.cpp.
if (argc < 1)
return usage("interleave-commits: missing <svn2git-db>", cmd);
mmapped_file svn2git;
unsigned char svn2git_magic[] = {'s', 2, 'g', 0xd, 0xb, 'm', 0xa, 'p'};
if (svn2git.init(argv[0]) ||
svn2git.num_bytes < (long)sizeof(svn2git_magic) ||
memcmp(svn2git_magic, svn2git.bytes, sizeof(svn2git_magic)))
return usage("invalid <svn2git-db>", cmd);
--argc, ++argv;
commit_interleaver interleaver(db, svn2git);
if (argc < 1)
return usage("interleave-commits: missing <head>", cmd);
textual_sha1 head;
if (head.from_input(*argv))
return usage("invalid sha1 for <head>", cmd);
interleaver.set_initial_head(head);
--argc, ++argv;
if (argc < 1)
return usage("interleave-commits: missing (<ref>:<dir>)+", cmd);
if (argc > dir_mask::max_size)
return usage("interleave-commits: too many dirs (max: " +
std::to_string(dir_mask::max_size) + ")",
cmd);
// Parse refs and directories.
auto parse_sha1 = [&interleaver](const char *¤t, sha1_ref &sha1) {
// TODO: add a testcase where sha1 is non-zero.
textual_sha1 text;
if (text.from_input(current, ¤t))
return 1;
sha1 = interleaver.sha1s.lookup(text);
return 0;
};
auto try_parse_ch = [](const char *¤t, int ch) {
if (*current != ch)
return 1;
++current;
return 0;
};
bool was_repeated_head_specified = false;
for (; argc; ++argv, --argc) {
const char *arg = *argv;
sha1_ref head;
bool is_tracked = false;
bool is_repeat = false;
// Skip over "--" and break out to continue to goals.
if (!strcmp(arg, "--")) {
++argv, --argc;
break;
}
if (try_parse_ch(arg, '-')) {
is_tracked = true;
if (!try_parse_ch(arg, '%'))
is_repeat = true;
}
if ((is_tracked && !is_repeat && parse_sha1(arg, head)) ||
try_parse_ch(arg, ':'))
return error("invalid <sha1>:... in '" + std::string(*argv) + "'");
if (!try_parse_ch(arg, '%')) {
if (*arg)
return error("invalid junk after '%' in '" + std::string(*argv) +
"'");
// This is the head for all of the repeated dirs.
if (was_repeated_head_specified)
return error("repeated head already specified");
was_repeated_head_specified = true;
interleaver.repeated_head = head;
continue;
}
int d = -1;
bool is_new = false;
if (interleaver.dirs.add_dir(arg, is_new, d))
return error("invalid ...:<dir> in '" + std::string(*argv) + "'");
if (!is_new)
return usage("duplicate <dir> '" + std::string(arg) + "'", cmd);
if (!is_tracked)
continue;
interleaver.dirs.tracked_dirs.set(d);
interleaver.dirs.set_head(d, head);
if (is_repeat) {
interleaver.dirs.repeated_dirs.set(d);
interleaver.dirs.list[d].is_repeated = true;
}
}
if (was_repeated_head_specified && !interleaver.dirs.repeated_dirs.bits.any())
return usage("head specified for repeated dirs, but no dirs", cmd);
if (!was_repeated_head_specified && interleaver.dirs.repeated_dirs.bits.any())
return usage("repeated dirs specified, but missing head", cmd);
if (interleaver.repeated_head)
interleaver.dirs.active_dirs.bits |= interleaver.dirs.repeated_dirs.bits;
// Create sources now that dirs are stable.
interleaver.initialize_sources();
// Parse goals.
for (; argc; ++argv, --argc) {
const char *arg = *argv;
sha1_ref goal;
if (parse_sha1(arg, goal) || try_parse_ch(arg, ':'))
return usage("invalid <sha1>:... in '" + std::string(*argv) + "'", cmd);
if (!goal)
return usage("invalid null goal in '" + std::string(*argv) + "'", cmd);
if (arg[0] == '%' && !arg[1]) {
if (!interleaver.repeat)
return usage("goal set for undeclared repeat '%'", cmd);
if (interleaver.repeat->goal &&
interleaver.repeat->goal != interleaver.repeat->head)
return usage("two goals for repeat '%'", cmd);
interleaver.repeat->goal = goal;
continue;
}
bool found = false;
int d = interleaver.dirs.lookup_dir(arg, found);
if (!found)
return usage("unknown <dir> '" + std::string(arg) + "'", cmd);
if (!interleaver.dirs.tracked_dirs.test(d))
return usage("untracked <dir> '" + std::string(arg) + "'", cmd);
if (interleaver.dirs.repeated_dirs.test(d))
return usage("cannot have goal for repeat <dir> '" + std::string(arg)
+ "'",
cmd);
auto &source = interleaver.q.sources[interleaver.dirs.list[d].source_index];
if (source.goal && source.goal != source.head)
return usage("two goals for <dir> '" + std::string(arg) + "'", cmd);
source.goal = goal;
}
for (auto &source : interleaver.q.sources)
if (!source.goal)
return usage(std::string("missing goal for <dir> '") +
(source.is_repeat
? "-"
: interleaver.dirs.list[source.dir_index].name) +
"'", cmd);
return interleaver.run();
}