From cd811419cb76c24f5ca72bde3746301272e97eee Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 13 Nov 2020 18:32:28 -0500 Subject: [PATCH] added git fetch and checkout fixed has_remote trying to fetch stuff pr: implemented remote method --- src/cmd/commands.cpp | 2 +- src/cmd/commands.h | 6 +- src/cmd/pr.cpp | 168 ++++++++++++++++++++++++++++--------------- src/tools/git.cpp | 68 +++++++++++++++--- src/tools/tools.h | 13 +++- 5 files changed, 187 insertions(+), 70 deletions(-) diff --git a/src/cmd/commands.cpp b/src/cmd/commands.cpp index be286ee..7bcfeb1 100644 --- a/src/cmd/commands.cpp +++ b/src/cmd/commands.cpp @@ -40,7 +40,7 @@ void help(const clipp::group& g, const std::string& more) << "Options:\n" << clipp::documentation(g, options_df) << "\n\n" - << "To use global options with command options, make sure command " + << "To use global options with command options, make sure command \n" << "options are together, with no global options in the middle.\n"; if (!more.empty()) diff --git a/src/cmd/commands.h b/src/cmd/commands.h index d1a1bbf..c2cd550 100644 --- a/src/cmd/commands.h +++ b/src/cmd/commands.h @@ -244,6 +244,7 @@ public: protected: clipp::group do_group() override; + std::string do_doc() override; int do_run() override; private: @@ -254,13 +255,12 @@ private: std::pair parse_pr( const std::string& pr) const; - int get_pr_branch(); - int do_apply(); int do_remote(); int do_fetch(); - url get_diff_url(const modorganizer* task, std::string pr); + nlohmann::json get_pr_info(const modorganizer* task, const std::string& pr); + url get_diff_url(const modorganizer* task, const std::string& pr); }; diff --git a/src/cmd/pr.cpp b/src/cmd/pr.cpp index 0a27e92..9c3a402 100644 --- a/src/cmd/pr.cpp +++ b/src/cmd/pr.cpp @@ -5,6 +5,13 @@ namespace mob { +std::string read_file(const fs::path& p) +{ + std::ifstream t(p); + return {std::istreambuf_iterator(t), std::istreambuf_iterator()}; +} + + pr_command::pr_command() : command(requires_options | handle_sigint), method_("apply") { @@ -40,6 +47,20 @@ clipp::group pr_command::do_group() % "PR to apply, must be `task/pr`, such as `modorganizer/123`"; } +std::string pr_command::do_doc() +{ + return + "Methods:\n" + " - apply: retrieves the .diff file for this PR and runs\n" + " `git apply` with it, which patches the local repo and\n" + " leaves the changes uncommitted\n" + "\n" + " - remote: adds a new remote to the local repo that matches the \n" + " PR's origin and checks out the PR's branch from it\n" + "\n" + " - fetch: ??"; +} + int pr_command::do_run() { if (method_ == "apply") @@ -96,63 +117,8 @@ std::pair pr_command::parse_pr( return {task, pr_number}; } -int pr_command::get_pr_branch() -{ - if (pr_.empty()) - return 0; - - if (github_token_.empty()) - { - u8cerr << "missing --github-token\n"; - return 1; - } - - auto&& [task, pr] = parse_pr(pr_); - - if (!task) - return 1; - - const url u(::fmt::format( - "https://api.github.com/repos/{}/{}/pulls/{}", - task->org(), task->repo(), pr)); - - curl_downloader dl; - - dl - .url(u) - .header("Authorization", "token " + github_token_) - .start() - .join(); - - if (!dl.ok()) - { - u8cerr << "getting pr failed\n"; - return 1; - } - - const auto output = dl.steal_output(); - u8cout << output << "\n"; - - - nlohmann::json j(output); - - const std::string diff_url = j["diff_url"]; - - return 0; -} - -url pr_command::get_diff_url(const modorganizer* task, std::string pr) -{ - return ::fmt::format( - "https://github.com/{}/{}/pull/{}.diff", - task->org(), task->repo(), pr); -} - int pr_command::do_apply() { - if (pr_.empty()) - return 0; - auto&& [task, pr] = parse_pr(pr_); if (!task) return 1; @@ -180,7 +146,59 @@ int pr_command::do_apply() int pr_command::do_remote() { - return 0; + auto&& [task, pr] = parse_pr(pr_); + if (!task) + return 1; + + try + { + u8cout << "looking for pr " << pr << " in " << task->name() << "\n"; + + //const auto json = get_pr_info(task, pr); + auto json = nlohmann::json::parse(read_file("c:\\tmp\\" + pr + ".json")); + if (json.empty()) + return 1; + + const std::string repo = json["head"]["repo"]["name"]; + const std::string org = json["head"]["repo"]["owner"]["login"]; + const std::string remote_name = org; + const std::string branch = json["head"]["ref"]; + + u8cout + << "found pr: " + << "repo=" << repo << " " + << "org=" << org << " " + << "branch=" << branch << "\n"; + + if (git::has_remote(task->this_source_path(), remote_name)) + { + u8cout << "remote already exists\n"; + } + else + { + u8cout << "adding remote " << remote_name << "\n"; + + git::add_remote( + task->this_source_path(), + remote_name, org, {}, false, + "https://github.com/{}/{}"); + } + + u8cout << "fetching from " << remote_name << "\n"; + git::fetch(task->this_source_path(), remote_name, branch); + + u8cout << "checking out " << remote_name << "/" << branch << "\n"; + git::checkout( + task->this_source_path(), + ::fmt::format("{}/{}", remote_name, branch)); + + return 0; + } + catch(std::exception& e) + { + u8cerr << e.what() << "\n"; + return 1; + } } int pr_command::do_fetch() @@ -188,4 +206,42 @@ int pr_command::do_fetch() return 0; } +url pr_command::get_diff_url(const modorganizer* task, const std::string& pr) +{ + return ::fmt::format( + "https://github.com/{}/{}/pull/{}.diff", + task->org(), task->repo(), pr); +} + +nlohmann::json pr_command::get_pr_info( + const modorganizer* task, const std::string& pr) +{ + if (github_token_.empty()) + { + u8cerr << "missing --github-token\n"; + return {}; + } + + const url u(::fmt::format( + "https://api.github.com/repos/{}/{}/pulls/{}", + task->org(), task->repo(), pr)); + + curl_downloader dl; + + dl + .url(u) + .header("Authorization", "token " + github_token_) + .start() + .join(); + + if (!dl.ok()) + { + u8cerr << "failed to get pr info from github\n"; + return {}; + } + + const auto output = dl.steal_output(); + return nlohmann::json::parse(output); +} + } // namespace diff --git a/src/tools/git.cpp b/src/tools/git.cpp index d6dbd7c..d5891d7 100644 --- a/src/tools/git.cpp +++ b/src/tools/git.cpp @@ -7,6 +7,8 @@ namespace mob { constexpr git::ops no_op(git::ops(0)); +const std::string default_github_url_pattern = "git@github.com:{}/{}"; + git::git(ops o) : basic_process_runner("git"), op_(o) @@ -68,9 +70,17 @@ void git::ignore_ts(const fs::path& repo, bool b) .do_ignore_ts(); } +bool git::has_remote(const fs::path& repo, const std::string& name) +{ + git g(no_op); + g.root(repo); + return g.has_remote(name); +} + void git::add_remote( const fs::path& repo, const std::string& remote_name, - const std::string& username, const std::string& key, bool push_default) + const std::string& username, const std::string& key, bool push_default, + const std::string& url_pattern) { git g(no_op); g.root(repo); @@ -79,7 +89,7 @@ void git::add_remote( if (!g.has_remote(remote_name)) { - g.add_remote(remote_name, make_url(username, gf)); + g.add_remote(remote_name, make_url(username, gf, url_pattern)); if (push_default) g.set_config("remote.pushdefault", remote_name); @@ -130,6 +140,45 @@ void git::apply(const std::string& diff) execute_and_join(); } +void git::fetch( + const fs::path& p, const std::string& remote, const std::string& branch) +{ + git g(no_op); + g.root(p); + g.fetch(remote, branch); +} + +void git::fetch(const std::string& remote, const std::string& branch) +{ + process_ = make_process() + .arg("fetch") + .arg("-q") + .arg(remote) + .arg(branch) + .cwd(root_); + + execute_and_join(); +} + +void git::checkout(const fs::path& p, const std::string& what) +{ + git g(no_op); + g.root(p); + g.checkout(what); +} + +void git::checkout(const std::string& what) +{ + process_ = make_process() + .arg("-c", "advice.detachedHead=false") + .arg("checkout") + .arg("-q") + .arg(what) + .cwd(root_); + + execute_and_join(); +} + fs::path git::binary() { return conf::tool_by_name("git"); @@ -447,9 +496,8 @@ bool git::has_remote(const std::string& name) process_ = make_process() .flags(process::allow_failure) .stderr_level(context::level::debug) - .arg("remote") - .arg("show") - .arg(name) + .arg("config") + .arg("remote." + name + ".url") .cwd(root_); return (execute_and_join() == 0); @@ -613,9 +661,13 @@ std::string git::git_file() } std::string git::make_url( - const std::string& org, const std::string& git_file) -{ - return "git@github.com:" + org + "/" + git_file; + const std::string& org, const std::string& git_file, + const std::string& url_pattern) + { + const std::string pattern = + url_pattern.empty() ? default_github_url_pattern : url_pattern; + + return fmt::format(pattern, org, git_file); } diff --git a/src/tools/tools.h b/src/tools/tools.h index a8fdc2e..1b94dfe 100644 --- a/src/tools/tools.h +++ b/src/tools/tools.h @@ -174,16 +174,22 @@ public: static void ignore_ts(const fs::path& repo, bool b); + static bool has_remote(const fs::path& repo, const std::string& name); + static void add_remote( const fs::path& repo, const std::string& remote_name, const std::string& org, const std::string& key, - bool push_default); + bool push_default, const std::string& url_pattern={}); static bool is_git_repo(const fs::path& p); static bool branch_exists(const mob::url& u, const std::string& name); static void init_repo(const fs::path& p); static void apply(const fs::path& p, const std::string& diff); + static void fetch( + const fs::path& p, + const std::string& remote, const std::string& branch); + static void checkout(const fs::path& p, const std::string& what); git& url(const mob::url& u); @@ -240,6 +246,8 @@ private: void set_remote_push(const std::string& remote, const std::string& url); void set_assume_unchanged(const fs::path& relative_file, bool on); void apply(const std::string& diff); + void fetch(const std::string& remote, const std::string& branch); + void checkout(const std::string& what); bool is_repo(); bool branch_exists(); bool has_uncommitted_changes(); @@ -249,7 +257,8 @@ private: std::string git_file(); static std::string make_url( - const std::string& org, const std::string& git_file); + const std::string& org, const std::string& git_file, + const std::string& url_pattern={}); };