// panel_worktrees.cpp — lists git worktrees via `git worktree list --porcelain`. // // Read-only MVP: shows path, head, branch. Future work: create/remove from // inside the panel (TODO in app.md ## Gotchas). #include "panels.h" #include "core/icons_tabler.h" #include #include #include #include #include namespace kanban_cpp { namespace { struct WT { std::string path; std::string head; std::string branch; }; std::vector scan_worktrees() { std::vector out; #ifdef _WIN32 FILE* fp = _popen("git worktree list --porcelain 2>nul", "r"); #else FILE* fp = popen("git worktree list --porcelain 2>/dev/null", "r"); #endif if (!fp) return out; std::array buf; WT cur; while (std::fgets(buf.data(), static_cast(buf.size()), fp)) { std::string line(buf.data()); while (!line.empty() && (line.back() == '\n' || line.back() == '\r')) line.pop_back(); if (line.empty()) { if (!cur.path.empty()) out.push_back(cur); cur = WT(); continue; } if (line.rfind("worktree ", 0) == 0) cur.path = line.substr(9); else if (line.rfind("HEAD ", 0) == 0) cur.head = line.substr(5); else if (line.rfind("branch ", 0) == 0) cur.branch = line.substr(7); } if (!cur.path.empty()) out.push_back(cur); #ifdef _WIN32 _pclose(fp); #else pclose(fp); #endif return out; } } // namespace void draw_worktrees(AppState& /*s*/, bool* p_open) { if (!ImGui::Begin(TI_GIT_BRANCH " Worktrees", p_open)) { ImGui::End(); return; } static std::vector wts; static bool first = true; if (first) { wts = scan_worktrees(); first = false; } if (ImGui::Button(TI_REFRESH " Rescan")) wts = scan_worktrees(); ImGui::SameLine(); ImGui::TextDisabled("%zu worktrees", wts.size()); ImGui::Separator(); if (ImGui::BeginTable("##wts", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) { ImGui::TableSetupColumn("Branch"); ImGui::TableSetupColumn("HEAD"); ImGui::TableSetupColumn("Path"); ImGui::TableHeadersRow(); for (const auto& w : wts) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::TextUnformatted(w.branch.empty() ? "(detached)" : w.branch.c_str()); ImGui::TableSetColumnIndex(1); ImGui::TextUnformatted(w.head.substr(0, 10).c_str()); ImGui::TableSetColumnIndex(2); ImGui::TextUnformatted(w.path.c_str()); } ImGui::EndTable(); } ImGui::End(); } } // namespace kanban_cpp