diff --git a/src/cmd/commands.h b/src/cmd/commands.h index 2b6e972..69879b3 100644 --- a/src/cmd/commands.h +++ b/src/cmd/commands.h @@ -386,7 +386,8 @@ private: none = 0, set_remotes, add_remote, - ignore_ts + ignore_ts, + branches }; modes mode_ = modes::none; @@ -398,6 +399,7 @@ private: bool tson_ = false; bool nopush_ = false; bool push_default_ = false; + bool all_branches_ = false; void do_set_remotes(); void do_set_remotes(const fs::path& r); @@ -408,6 +410,8 @@ private: void do_ignore_ts(); void do_ignore_ts(const fs::path& r); + void do_branches(); + std::vector get_repos() const; }; diff --git a/src/cmd/git.cpp b/src/cmd/git.cpp index 667da73..85f5abe 100644 --- a/src/cmd/git.cpp +++ b/src/cmd/git.cpp @@ -85,6 +85,14 @@ clipp::group git_command::do_group() clipp::command("off").set(tson_, false) ) ) + + | + + "branches" % + (clipp::command("branches").set(mode_, modes::branches), + clipp::option("-a", "--all").set(all_branches_) + % "shows all branches, including those on master" + ) ); } @@ -110,6 +118,12 @@ int git_command::do_run() break; } + case modes::branches: + { + do_branches(); + break; + } + case modes::none: default: u8cerr << "bad git mode " << static_cast(mode_) << "\n"; @@ -137,7 +151,11 @@ std::string git_command::do_doc() " remote with the same name already exists, nothing happens.\n" "\n" "ignore-ts\n" - " Toggles the --assume-changed status of all .ts files in all repos."; + " Toggles the --assume-changed status of all .ts files in all repos.\n" + "\n" + "branches\n" + " Lists all git repos that are not on master. With -a, show all \n" + " repos and their current branch."; } void git_command::do_set_remotes() @@ -213,6 +231,25 @@ void git_command::do_ignore_ts(const fs::path& r) git::ignore_ts(r, tson_); } +void git_command::do_branches() +{ + std::vector> v; + + for (auto&& r : get_repos()) + { + const auto b = git::current_branch(r); + if (b == "master" && !all_branches_) + continue; + + if (b.empty()) + v.push_back({r.filename().string(), "detached head"}); + else + v.push_back({r.filename().string(), b}); + } + + u8cout << table(v, 0, 3) << "\n"; +} + std::vector git_command::get_repos() const { std::vector v; diff --git a/src/tools/git.cpp b/src/tools/git.cpp index d5891d7..5ee623f 100644 --- a/src/tools/git.cpp +++ b/src/tools/git.cpp @@ -179,6 +179,26 @@ void git::checkout(const std::string& what) execute_and_join(); } +std::string git::current_branch(const fs::path& p) +{ + git g(no_op); + g.root(p); + return g.current_branch(); +} + +std::string git::current_branch() +{ + process_ = make_process() + .stdout_flags(process::keep_in_string) + .arg("branch") + .arg("--show-current") + .cwd(root_); + + execute_and_join(); + + return trim_copy(process_.stdout_string()); +} + fs::path git::binary() { return conf::tool_by_name("git"); diff --git a/src/tools/tools.h b/src/tools/tools.h index 1b94dfe..9001763 100644 --- a/src/tools/tools.h +++ b/src/tools/tools.h @@ -191,6 +191,8 @@ public: const std::string& remote, const std::string& branch); static void checkout(const fs::path& p, const std::string& what); + static std::string current_branch(const fs::path& p); + git& url(const mob::url& u); git& branch(const std::string& name); @@ -248,6 +250,7 @@ private: void apply(const std::string& diff); void fetch(const std::string& remote, const std::string& branch); void checkout(const std::string& what); + std::string current_branch(); bool is_repo(); bool branch_exists(); bool has_uncommitted_changes();