Index: tools/clang-diff/ClangDiff.cpp =================================================================== --- tools/clang-diff/ClangDiff.cpp +++ tools/clang-diff/ClangDiff.cpp @@ -16,6 +16,7 @@ #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Program.h" using namespace llvm; using namespace clang; @@ -33,9 +34,9 @@ cl::desc("Print the internal representation of the AST as JSON."), cl::init(false), cl::cat(ClangDiffCategory)); -static cl::opt - PrintMatches("dump-matches", cl::desc("Print the matched nodes."), - cl::init(false), cl::cat(ClangDiffCategory)); +static cl::opt PrintMatches("dump-matches", + cl::desc("Print the matched nodes."), + cl::init(false), cl::cat(ClangDiffCategory)); static cl::opt HtmlDiff("html", cl::desc("Output a side-by-side diff in HTML."), @@ -55,6 +56,11 @@ cl::Optional, cl::init(""), cl::cat(ClangDiffCategory)); +static cl::opt + GitRevision("git-rev", cl::desc("Compare the file from a checkout of this " + "revision with the current version."), + cl::Optional, cl::init(""), cl::cat(ClangDiffCategory)); + static cl::opt MaxSize("s", cl::desc(""), cl::Optional, cl::init(-1), cl::cat(ClangDiffCategory)); @@ -438,6 +444,19 @@ } } +std::string Exec(const char *Command) { + char Buffer[128]; + std::string Result; + std::shared_ptr Pipe(popen(Command, "r"), pclose); + if (!Pipe) + return Result; + while (!feof(Pipe.get())) { + if (fgets(Buffer, 128, Pipe.get()) != nullptr) + Result += Buffer; + } + return Result; +} + int main(int argc, const char **argv) { std::string ErrorMessage; std::unique_ptr CommonCompilations = @@ -473,13 +492,40 @@ return 0; } - if (DestinationPath.empty()) { + if (DestinationPath.empty() && GitRevision.empty()) { llvm::errs() << "Error: Exactly two paths are required.\n"; return 1; } - std::unique_ptr Src = getAST(CommonCompilations, SourcePath); - std::unique_ptr Dst = getAST(CommonCompilations, DestinationPath); + std::unique_ptr Src, Dst; + + if (!GitRevision.empty()) { + std::string CurrentRevision, Git; + auto ErrorOrGit = llvm::sys::findProgramByName("git"); + if (!ErrorOrGit) { + llvm::errs() << "Error: Could not find git executable.\n"; + return 1; + } + Git = ErrorOrGit.get(); + CurrentRevision = Exec("git rev-parse HEAD"); + *std::find(CurrentRevision.begin(), CurrentRevision.end(), '\n') = '\0'; + const char *Checkout[] = {"git", "checkout", GitRevision.data(), nullptr}; + if (llvm::sys::ExecuteAndWait(Git, Checkout)) { + llvm::errs() << "Error: Failed to checkout " << GitRevision << "\n"; + return 1; + } + Src = getAST(CommonCompilations, SourcePath); + Checkout[2] = CurrentRevision.data(); + if (llvm::sys::ExecuteAndWait(Git, Checkout)) { + llvm::errs() << "Error: Failed to checkout " << CurrentRevision << "\n"; + return 1; + } + Dst = getAST(CommonCompilations, SourcePath); + } else { + Src = getAST(CommonCompilations, SourcePath); + Dst = getAST(CommonCompilations, DestinationPath); + } + if (!Src || !Dst) return 1;