diff --git a/README.md b/README.md index f767c5c..0cd7291 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,12 @@ WizardMerge uses a multi-frontend architecture with a high-performance C++ backe - **Build System**: CMake + Ninja - **Package Manager**: Conan - **Web Framework**: Drogon -- **Features**: Three-way merge algorithm, conflict detection, auto-resolution, HTTP API +- **Features**: + - Three-way merge algorithm + - Conflict detection and auto-resolution + - HTTP API endpoints + - GitHub Pull Request integration + - Pull request conflict resolution ### Frontends @@ -96,6 +101,45 @@ ninja See [frontends/cli/README.md](frontends/cli/README.md) for details. +## Pull Request Conflict Resolution + +WizardMerge can automatically resolve conflicts in GitHub pull requests using advanced merge algorithms. + +### Using the CLI + +```sh +# Resolve conflicts in a pull request +./wizardmerge-cli-frontend pr-resolve --url https://github.com/owner/repo/pull/123 + +# With GitHub token for private repos +./wizardmerge-cli-frontend pr-resolve --url https://github.com/owner/repo/pull/123 --token ghp_xxx + +# Or use environment variable +export GITHUB_TOKEN=ghp_xxx +./wizardmerge-cli-frontend pr-resolve --url https://github.com/owner/repo/pull/123 +``` + +### Using the HTTP API + +```sh +# POST /api/pr/resolve +curl -X POST http://localhost:8080/api/pr/resolve \ + -H "Content-Type: application/json" \ + -d '{ + "pr_url": "https://github.com/owner/repo/pull/123", + "github_token": "ghp_xxx", + "create_branch": true, + "branch_name": "wizardmerge-resolved-pr-123" + }' +``` + +The API will: +1. Parse the PR URL and fetch PR metadata from GitHub +2. Retrieve base and head versions of all modified files +3. Apply the three-way merge algorithm to each file +4. Auto-resolve conflicts using heuristics +5. Return merged content with conflict status + ## Research Foundation WizardMerge is based on research from The University of Hong Kong achieving: diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt index c12525c..706eb92 100644 --- a/backend/CMakeLists.txt +++ b/backend/CMakeLists.txt @@ -12,11 +12,20 @@ find_package(GTest QUIET) find_package(CURL QUIET) # Library sources -add_library(wizardmerge +set(WIZARDMERGE_SOURCES src/merge/three_way_merge.cpp - src/git/github_client.cpp ) +# Add git sources only if CURL is available +if(CURL_FOUND) + list(APPEND WIZARDMERGE_SOURCES src/git/github_client.cpp) + message(STATUS "CURL found - including GitHub API client") +else() + message(WARNING "CURL not found - GitHub API features will be unavailable") +endif() + +add_library(wizardmerge ${WIZARDMERGE_SOURCES}) + target_include_directories(wizardmerge PUBLIC $ @@ -30,11 +39,18 @@ endif() # Executable (only if Drogon is found) if(Drogon_FOUND) - add_executable(wizardmerge-cli + set(CLI_SOURCES src/main.cpp src/controllers/MergeController.cc - src/controllers/PRController.cc ) + + # Add PR controller only if CURL is available + if(CURL_FOUND) + list(APPEND CLI_SOURCES src/controllers/PRController.cc) + message(STATUS "CURL found - including PR resolution endpoint") + endif() + + add_executable(wizardmerge-cli ${CLI_SOURCES}) target_link_libraries(wizardmerge-cli PRIVATE wizardmerge Drogon::Drogon) @@ -50,10 +66,15 @@ endif() # Tests (if GTest is available) if(GTest_FOUND) enable_testing() - add_executable(wizardmerge-tests - tests/test_three_way_merge.cpp - tests/test_github_client.cpp - ) + + set(TEST_SOURCES tests/test_three_way_merge.cpp) + + # Add github client tests only if CURL is available + if(CURL_FOUND) + list(APPEND TEST_SOURCES tests/test_github_client.cpp) + endif() + + add_executable(wizardmerge-tests ${TEST_SOURCES}) target_link_libraries(wizardmerge-tests PRIVATE wizardmerge GTest::gtest_main) include(GoogleTest) diff --git a/backend/README.md b/backend/README.md index 2e086aa..1603b21 100644 --- a/backend/README.md +++ b/backend/README.md @@ -124,6 +124,8 @@ backend/ - Auto-resolution of common patterns - HTTP API server using Drogon framework - JSON-based request/response +- GitHub Pull Request integration (Phase 1.2) +- Pull request conflict resolution via API ## API Usage @@ -168,6 +170,58 @@ curl -X POST http://localhost:8080/api/merge \ }' ``` +### POST /api/pr/resolve + +Resolve conflicts in a GitHub pull request. + +**Request:** +```json +{ + "pr_url": "https://github.com/owner/repo/pull/123", + "github_token": "ghp_xxx", + "create_branch": true, + "branch_name": "wizardmerge-resolved-pr-123" +} +``` + +**Response:** +```json +{ + "success": true, + "pr_info": { + "number": 123, + "title": "Feature: Add new functionality", + "base_ref": "main", + "head_ref": "feature-branch", + "mergeable": false + }, + "resolved_files": [ + { + "filename": "src/example.cpp", + "status": "modified", + "had_conflicts": true, + "auto_resolved": true, + "merged_content": ["line1", "line2", "..."] + } + ], + "total_files": 5, + "resolved_count": 4, + "failed_count": 0 +} +``` + +**Example with curl:** +```sh +curl -X POST http://localhost:8080/api/pr/resolve \ + -H "Content-Type: application/json" \ + -d '{ + "pr_url": "https://github.com/owner/repo/pull/123", + "github_token": "ghp_xxx" + }' +``` + +**Note:** Requires libcurl to be installed. The GitHub token is optional for public repositories but required for private ones. + ## Deployment ### Production Deployment with Docker diff --git a/backend/build.sh b/backend/build.sh index 5a2e46e..ea8e5e8 100755 --- a/backend/build.sh +++ b/backend/build.sh @@ -20,10 +20,16 @@ if ! pkg-config --exists drogon 2>/dev/null && ! ldconfig -p 2>/dev/null | grep echo " Option 2: Use Docker: docker-compose up --build" echo " Option 3: Use Conan: conan install . --output-folder=build --build=missing" echo - read -p "Continue building without Drogon? (y/n) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - exit 1 + + # Skip prompt if in non-interactive mode or CI + if [[ -n "$CI" ]] || [[ -n "$WIZARDMERGE_AUTO_BUILD" ]] || [[ ! -t 0 ]]; then + echo "Non-interactive mode detected, continuing without Drogon..." + else + read -p "Continue building without Drogon? (y/n) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 + fi fi fi diff --git a/backend/conanfile.py b/backend/conanfile.py index b396fb3..40462ee 100644 --- a/backend/conanfile.py +++ b/backend/conanfile.py @@ -18,7 +18,7 @@ class WizardMergeConan(ConanFile): exports_sources = "CMakeLists.txt", "src/*", "include/*" # Dependencies - requires = ["drogon/1.9.3"] + requires = ["drogon/1.9.3", "libcurl/8.4.0"] generators = "CMakeDeps", "CMakeToolchain" diff --git a/spec/WizardMergeSpec.tla b/spec/WizardMergeSpec.tla index 3111cef..9aca5ec 100644 --- a/spec/WizardMergeSpec.tla +++ b/spec/WizardMergeSpec.tla @@ -15,6 +15,11 @@ EXTENDS Naturals, FiniteSets * Identical changes from both sides * Whitespace-only differences - Command-line interface (wizardmerge-cli) + - Pull request URL processing and conflict resolution: + * Parse GitHub PR URLs + * Fetch PR data via GitHub API + * Apply merge algorithm to PR files + * HTTP API endpoint for PR resolution NOT YET IMPLEMENTED (Future phases): - Dependency graph construction (SDG analysis) @@ -22,11 +27,26 @@ EXTENDS Naturals, FiniteSets - Edge classification (safe vs. violated) - Fine-grained DCB (Definition-Code Block) tracking - Mirror mapping and matching + - Git branch creation for resolved PRs The current implementation in backend/src/merge/three_way_merge.cpp provides a foundation for the full dependency-aware algorithm specified here. Future phases will enhance it with the SDG analysis, edge classification, and dependency-aware conflict resolution described in this specification. + + PR Resolution Workflow (Phase 1.2): + The PR resolution feature extends the core merge algorithm to work with + GitHub pull requests. The workflow is: + 1. Accept PR URL: Parse URL to extract owner, repo, and PR number + 2. Fetch PR metadata: Use GitHub API to retrieve PR information + 3. Fetch file versions: Retrieve base and head versions of modified files + 4. Apply merge algorithm: For each file, perform three-way merge + 5. Auto-resolve conflicts: Apply heuristic resolution where possible + 6. Return results: Provide merged content and conflict status + + This workflow enables batch processing of PR conflicts using the same + dependency-aware merge principles, with future integration planned for + automatic branch creation and PR updates. *) (* @@ -316,4 +336,98 @@ Inv == THEOREM Spec => []Inv +(***************************************************************************) +(* Pull Request Resolution Specification (Phase 1.2) *) +(***************************************************************************) + +(* + This section extends the core merge specification to model the PR resolution + workflow. It describes how WizardMerge processes GitHub pull requests to + identify and resolve conflicts across multiple files. +*) + +CONSTANTS + (* + PR_FILES: the set of all files in the pull request + *) + PR_FILES, + + (* + FileStatus: maps each file to its modification status in the PR + Possible values: "modified", "added", "removed", "renamed" + *) + FileStatus, + + (* + BaseSHA, HeadSHA: commit identifiers for base and head of the PR + *) + BaseSHA, HeadSHA + +(* + A file is resolvable if it was modified (not removed) and we can fetch + both its base and head versions. +*) +Resolvable(f) == + FileStatus[f] \in {"modified", "added"} + +(* + PR_MergeResult: for each file f in PR_FILES, we compute a merge result + using the three-way merge algorithm. This is a function from PR_FILES + to merge outcomes. + + Possible outcomes: + - "success": file merged without conflicts + - "conflict": file has unresolved conflicts + - "error": failed to fetch or process file + - "skipped": file was removed or not applicable +*) + +VARIABLE PR_MergeResults + +PR_Init == + PR_MergeResults = [f \in PR_FILES |-> "pending"] + +(* + Process a single file by applying the three-way merge algorithm. + This abstracts the actual merge computation but captures the key decision: + whether the file can be auto-resolved or requires manual intervention. +*) +ProcessFile(f) == + /\ PR_MergeResults[f] = "pending" + /\ IF ~Resolvable(f) + THEN PR_MergeResults' = [PR_MergeResults EXCEPT ![f] = "skipped"] + ELSE \/ PR_MergeResults' = [PR_MergeResults EXCEPT ![f] = "success"] + \/ PR_MergeResults' = [PR_MergeResults EXCEPT ![f] = "conflict"] + \/ PR_MergeResults' = [PR_MergeResults EXCEPT ![f] = "error"] + +(* + PR completion: all files have been processed +*) +PR_Complete == + \A f \in PR_FILES : PR_MergeResults[f] # "pending" + +(* + PR success metric: percentage of files successfully merged +*) +PR_SuccessRate == + LET successful == {f \in PR_FILES : PR_MergeResults[f] = "success"} + IN Cardinality(successful) * 100 \div Cardinality(PR_FILES) + +(* + PR resolution quality property: a "good" PR resolution is one where + all resolvable files are either successfully merged or marked as conflicts + (no errors in fetching or processing). +*) +GoodPRResolution == + \A f \in PR_FILES : + Resolvable(f) => PR_MergeResults[f] \in {"success", "conflict"} + +PR_Spec == + PR_Init /\ [][(\E f \in PR_FILES : ProcessFile(f))]_<> + +PR_Invariant == + PR_Complete => GoodPRResolution + +THEOREM PR_Spec => []PR_Invariant + =============================================================================