mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Move git utilities into a dedicated crate (#15564)
- create `codex-git-utils` and move the shared git helpers into it with file moves preserved for diff readability - move the `GitInfo` helpers out of `core` so stacked rollout work can depend on the shared crate without carrying its own git info module --------- Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com> Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
fc97092f75
commit
0f957a93cd
Generated
+26
-15
@@ -380,7 +380,7 @@ version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -391,7 +391,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1444,6 +1444,7 @@ dependencies = [
|
||||
"codex-features",
|
||||
"codex-feedback",
|
||||
"codex-file-search",
|
||||
"codex-git-utils",
|
||||
"codex-login",
|
||||
"codex-otel",
|
||||
"codex-protocol",
|
||||
@@ -1509,6 +1510,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"codex-experimental-api-macros",
|
||||
"codex-git-utils",
|
||||
"codex-protocol",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-cargo-bin",
|
||||
@@ -1643,7 +1645,7 @@ dependencies = [
|
||||
"clap",
|
||||
"codex-connectors",
|
||||
"codex-core",
|
||||
"codex-git",
|
||||
"codex-git-utils",
|
||||
"codex-utils-cargo-bin",
|
||||
"codex-utils-cli",
|
||||
"pretty_assertions",
|
||||
@@ -1768,6 +1770,7 @@ dependencies = [
|
||||
"codex-client",
|
||||
"codex-cloud-tasks-client",
|
||||
"codex-core",
|
||||
"codex-git-utils",
|
||||
"codex-login",
|
||||
"codex-tui",
|
||||
"codex-utils-cli",
|
||||
@@ -1794,7 +1797,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"codex-backend-client",
|
||||
"codex-git",
|
||||
"codex-git-utils",
|
||||
"diffy",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -1879,7 +1882,7 @@ dependencies = [
|
||||
"codex-execpolicy",
|
||||
"codex-features",
|
||||
"codex-file-search",
|
||||
"codex-git",
|
||||
"codex-git-utils",
|
||||
"codex-hooks",
|
||||
"codex-login",
|
||||
"codex-network-proxy",
|
||||
@@ -1993,6 +1996,7 @@ dependencies = [
|
||||
"codex-cloud-requirements",
|
||||
"codex-core",
|
||||
"codex-feedback",
|
||||
"codex-git-utils",
|
||||
"codex-otel",
|
||||
"codex-protocol",
|
||||
"codex-utils-absolute-path",
|
||||
@@ -2133,10 +2137,12 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-git"
|
||||
name = "codex-git-utils"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"codex-utils-absolute-path",
|
||||
"futures",
|
||||
"once_cell",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
@@ -2144,6 +2150,7 @@ dependencies = [
|
||||
"serde",
|
||||
"tempfile",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
"walkdir",
|
||||
]
|
||||
@@ -2389,7 +2396,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"codex-execpolicy",
|
||||
"codex-git",
|
||||
"codex-git-utils",
|
||||
"codex-utils-absolute-path",
|
||||
"codex-utils-image",
|
||||
"icu_decimal",
|
||||
@@ -2484,6 +2491,7 @@ dependencies = [
|
||||
"age",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"codex-git-utils",
|
||||
"codex-keyring-store",
|
||||
"keyring",
|
||||
"pretty_assertions",
|
||||
@@ -2554,6 +2562,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"clap",
|
||||
"codex-git-utils",
|
||||
"codex-protocol",
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -2620,6 +2629,7 @@ dependencies = [
|
||||
"codex-features",
|
||||
"codex-feedback",
|
||||
"codex-file-search",
|
||||
"codex-git-utils",
|
||||
"codex-login",
|
||||
"codex-otel",
|
||||
"codex-protocol",
|
||||
@@ -2713,6 +2723,7 @@ dependencies = [
|
||||
"codex-features",
|
||||
"codex-feedback",
|
||||
"codex-file-search",
|
||||
"codex-git-utils",
|
||||
"codex-login",
|
||||
"codex-otel",
|
||||
"codex-protocol",
|
||||
@@ -3836,7 +3847,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.5.2",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4081,7 +4092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5526,7 +5537,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6289,7 +6300,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6933,7 +6944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8355,7 +8366,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.11.0",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9788,7 +9799,7 @@ dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix 1.1.3",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11234,7 +11245,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@ members = [
|
||||
"v8-poc",
|
||||
"utils/absolute-path",
|
||||
"utils/cargo-bin",
|
||||
"utils/git",
|
||||
"git-utils",
|
||||
"utils/cache",
|
||||
"utils/image",
|
||||
"utils/json-to-toml",
|
||||
@@ -117,7 +117,7 @@ codex-experimental-api-macros = { path = "codex-experimental-api-macros" }
|
||||
codex-feedback = { path = "feedback" }
|
||||
codex-features = { path = "features" }
|
||||
codex-file-search = { path = "file-search" }
|
||||
codex-git = { path = "utils/git" }
|
||||
codex-git-utils = { path = "git-utils" }
|
||||
codex-hooks = { path = "hooks" }
|
||||
codex-keyring-store = { path = "keyring-store" }
|
||||
codex-linux-sandbox = { path = "linux-sandbox" }
|
||||
|
||||
@@ -14,8 +14,9 @@ workspace = true
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
codex-protocol = { workspace = true }
|
||||
codex-experimental-api-macros = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -4,6 +4,7 @@ mod jsonrpc_lite;
|
||||
mod protocol;
|
||||
mod schema_fixtures;
|
||||
|
||||
pub use codex_git_utils::GitSha;
|
||||
pub use experimental_api::*;
|
||||
pub use export::GenerateTsOptions;
|
||||
pub use export::generate_internal_json_schema;
|
||||
|
||||
@@ -14,16 +14,6 @@ use serde::Serialize;
|
||||
use strum_macros::Display;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, TS)]
|
||||
#[ts(type = "string")]
|
||||
pub struct GitSha(pub String);
|
||||
|
||||
impl GitSha {
|
||||
pub fn new(sha: &str) -> Self {
|
||||
Self(sha.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Authentication mode for OpenAI-backed providers.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Display, JsonSchema, TS)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_git_utils::GitSha;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -21,7 +22,6 @@ use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::protocol::common::AuthMode;
|
||||
use crate::protocol::common::GitSha;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -34,6 +34,7 @@ codex-cloud-requirements = { workspace = true }
|
||||
codex-core = { workspace = true }
|
||||
codex-exec-server = { workspace = true }
|
||||
codex-features = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-otel = { workspace = true }
|
||||
codex-shell-command = { workspace = true }
|
||||
codex-utils-cli = { workspace = true }
|
||||
|
||||
@@ -213,7 +213,6 @@ use codex_core::find_archived_thread_path_by_id_str;
|
||||
use codex_core::find_thread_name_by_id;
|
||||
use codex_core::find_thread_names_by_ids;
|
||||
use codex_core::find_thread_path_by_id_str;
|
||||
use codex_core::git_info::git_diff_to_remote;
|
||||
use codex_core::mcp::auth::discover_supported_scopes;
|
||||
use codex_core::mcp::auth::resolve_oauth_scopes;
|
||||
use codex_core::mcp::collect_mcp_snapshot;
|
||||
@@ -243,6 +242,7 @@ use codex_features::FEATURES;
|
||||
use codex_features::Feature;
|
||||
use codex_features::Stage;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_git_utils::git_diff_to_remote;
|
||||
use codex_login::ServerOptions as LoginServerOptions;
|
||||
use codex_login::ShutdownHandle;
|
||||
use codex_login::auth::login_with_chatgpt_auth_tokens;
|
||||
@@ -8224,7 +8224,7 @@ fn extract_conversation_summary(
|
||||
|
||||
fn map_git_info(git_info: &CoreGitInfo) -> ConversationGitInfo {
|
||||
ConversationGitInfo {
|
||||
sha: git_info.commit_hash.clone(),
|
||||
sha: git_info.commit_hash.as_ref().map(|sha| sha.0.clone()),
|
||||
branch: git_info.branch.clone(),
|
||||
origin_url: git_info.repository_url.clone(),
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ use codex_app_server_protocol::TurnStartParams;
|
||||
use codex_app_server_protocol::TurnStartResponse;
|
||||
use codex_app_server_protocol::UserInput;
|
||||
use codex_core::ARCHIVED_SESSIONS_SUBDIR;
|
||||
use codex_git_utils::GitSha;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::protocol::GitInfo as CoreGitInfo;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
@@ -959,7 +960,7 @@ async fn thread_list_includes_git_info() -> Result<()> {
|
||||
create_minimal_config(codex_home.path())?;
|
||||
|
||||
let git_info = CoreGitInfo {
|
||||
commit_hash: Some("abc123".to_string()),
|
||||
commit_hash: Some(GitSha::new("abc123")),
|
||||
branch: Some("main".to_string()),
|
||||
repository_url: Some("https://example.com/repo.git".to_string()),
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ use codex_app_server_protocol::ThreadStartResponse;
|
||||
use codex_app_server_protocol::ThreadStatus;
|
||||
use codex_core::ARCHIVED_SESSIONS_SUBDIR;
|
||||
use codex_core::state_db::reconcile_rollout;
|
||||
use codex_git_utils::GitSha;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::protocol::GitInfo as RolloutGitInfo;
|
||||
use codex_state::StateRuntime;
|
||||
@@ -378,7 +379,7 @@ async fn thread_metadata_update_can_clear_stored_git_fields() -> Result<()> {
|
||||
"Thread preview",
|
||||
Some("mock_provider"),
|
||||
Some(RolloutGitInfo {
|
||||
commit_hash: Some("abc123".to_string()),
|
||||
commit_hash: Some(GitSha::new("abc123")),
|
||||
branch: Some("feature/sidebar-pr".to_string()),
|
||||
repository_url: Some("git@example.com:openai/codex.git".to_string()),
|
||||
}),
|
||||
|
||||
@@ -17,7 +17,7 @@ codex-utils-cargo-bin = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
codex-git = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -2,6 +2,8 @@ use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
use codex_core::config::Config;
|
||||
use codex_git_utils::ApplyGitRequest;
|
||||
use codex_git_utils::apply_git_patch;
|
||||
use codex_utils_cli::CliConfigOverrides;
|
||||
|
||||
use crate::chatgpt_token::init_chatgpt_token_from_auth;
|
||||
@@ -57,13 +59,13 @@ pub async fn apply_diff_from_task(
|
||||
|
||||
async fn apply_diff(diff: &str, cwd: Option<PathBuf>) -> anyhow::Result<()> {
|
||||
let cwd = cwd.unwrap_or(std::env::current_dir().unwrap_or_else(|_| std::env::temp_dir()));
|
||||
let req = codex_git::ApplyGitRequest {
|
||||
let req = ApplyGitRequest {
|
||||
cwd,
|
||||
diff: diff.to_string(),
|
||||
revert: false,
|
||||
preflight: false,
|
||||
};
|
||||
let res = codex_git::apply_git_patch(&req)?;
|
||||
let res = apply_git_patch(&req)?;
|
||||
if res.exit_code != 0 {
|
||||
anyhow::bail!(
|
||||
"Git apply failed (applied={}, skipped={}, conflicts={})\nstdout:\n{}\nstderr:\n{}",
|
||||
|
||||
@@ -25,4 +25,4 @@ serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
thiserror = "2.0.17"
|
||||
codex-backend-client = { path = "../backend-client", optional = true }
|
||||
codex-git = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
|
||||
@@ -16,6 +16,8 @@ use chrono::Utc;
|
||||
|
||||
use codex_backend_client as backend;
|
||||
use codex_backend_client::CodeTaskDetailsResponseExt;
|
||||
use codex_git_utils::ApplyGitRequest;
|
||||
use codex_git_utils::apply_git_patch;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HttpClient {
|
||||
@@ -459,13 +461,13 @@ mod api {
|
||||
});
|
||||
}
|
||||
|
||||
let req = codex_git::ApplyGitRequest {
|
||||
let req = ApplyGitRequest {
|
||||
cwd: std::env::current_dir().unwrap_or_else(|_| std::env::temp_dir()),
|
||||
diff: diff.clone(),
|
||||
revert: false,
|
||||
preflight,
|
||||
};
|
||||
let r = codex_git::apply_git_patch(&req)
|
||||
let r = apply_git_patch(&req)
|
||||
.map_err(|e| CloudTaskError::Io(format!("git apply failed to run: {e}")))?;
|
||||
|
||||
let status = if r.exit_code == 0 {
|
||||
|
||||
@@ -27,4 +27,4 @@ pub use mock::MockClient;
|
||||
#[cfg(feature = "online")]
|
||||
pub use http::HttpClient;
|
||||
|
||||
// Reusable apply engine now lives in the shared crate `codex-git`.
|
||||
// Reusable apply engine now lives in the shared crate `codex-git-utils`.
|
||||
|
||||
@@ -22,6 +22,7 @@ codex-cloud-tasks-client = { path = "../cloud-tasks-client", features = [
|
||||
] }
|
||||
codex-client = { workspace = true }
|
||||
codex-core = { path = "../core" }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-login = { path = "../login" }
|
||||
codex-tui = { path = "../tui" }
|
||||
codex-utils-cli = { workspace = true }
|
||||
|
||||
@@ -10,6 +10,8 @@ pub use cli::Cli;
|
||||
use anyhow::anyhow;
|
||||
use chrono::Utc;
|
||||
use codex_cloud_tasks_client::TaskStatus;
|
||||
use codex_git_utils::current_branch_name;
|
||||
use codex_git_utils::default_branch_name;
|
||||
use owo_colors::OwoColorize;
|
||||
use owo_colors::Stream;
|
||||
use std::cmp::Ordering;
|
||||
@@ -119,11 +121,11 @@ struct RealGitInfo;
|
||||
#[async_trait::async_trait]
|
||||
impl GitInfoProvider for RealGitInfo {
|
||||
async fn default_branch_name(&self, path: &std::path::Path) -> Option<String> {
|
||||
codex_core::git_info::default_branch_name(path).await
|
||||
default_branch_name(path).await
|
||||
}
|
||||
|
||||
async fn current_branch_name(&self, path: &std::path::Path) -> Option<String> {
|
||||
codex_core::git_info::current_branch_name(path).await
|
||||
current_branch_name(path).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ codex-shell-command = { workspace = true }
|
||||
codex-skills = { workspace = true }
|
||||
codex-execpolicy = { workspace = true }
|
||||
codex-file-search = { workspace = true }
|
||||
codex-git = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-hooks = { workspace = true }
|
||||
codex-network-proxy = { workspace = true }
|
||||
codex-otel = { workspace = true }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::AuthManager;
|
||||
use crate::config::Config;
|
||||
use crate::default_client::create_client;
|
||||
use crate::git_info::collect_git_info;
|
||||
use crate::git_info::get_git_repo_root;
|
||||
use crate::plugins::PluginTelemetryMetadata;
|
||||
use codex_git_utils::collect_git_info;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_protocol::protocol::SkillScope;
|
||||
use serde::Serialize;
|
||||
use sha1::Digest;
|
||||
|
||||
@@ -235,7 +235,6 @@ pub(crate) struct PreviousTurnSettings {
|
||||
|
||||
use crate::exec_policy::ExecPolicyUpdateError;
|
||||
use crate::feedback_tags;
|
||||
use crate::git_info::get_git_repo_root;
|
||||
use crate::guardian::GuardianReviewSessionManager;
|
||||
use crate::hook_runtime::PendingInputHookDisposition;
|
||||
use crate::hook_runtime::inspect_pending_input;
|
||||
@@ -350,6 +349,7 @@ use crate::unified_exec::UnifiedExecProcessManager;
|
||||
use crate::util::backoff;
|
||||
use crate::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_async_utils::OrCancelExt;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_otel::TelemetryAuthMode;
|
||||
use codex_otel::metrics::names::THREAD_STARTED_METRIC;
|
||||
|
||||
@@ -39,7 +39,6 @@ use crate::config_loader::McpServerRequirement;
|
||||
use crate::config_loader::ResidencyRequirement;
|
||||
use crate::config_loader::Sourced;
|
||||
use crate::config_loader::load_config_layers_state;
|
||||
use crate::git_info::resolve_root_git_project_for_trust;
|
||||
use crate::memories::memory_root;
|
||||
use crate::model_provider_info::LEGACY_OLLAMA_CHAT_PROVIDER_ID;
|
||||
use crate::model_provider_info::LMSTUDIO_OSS_PROVIDER_ID;
|
||||
@@ -66,6 +65,7 @@ use codex_features::FeatureConfigSource;
|
||||
use codex_features::FeatureOverrides;
|
||||
use codex_features::Features;
|
||||
use codex_features::FeaturesToml;
|
||||
use codex_git_utils::resolve_root_git_project_for_trust;
|
||||
use codex_protocol::config_types::AltScreenMode;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::Personality;
|
||||
@@ -134,7 +134,7 @@ pub use service::ConfigService;
|
||||
pub use service::ConfigServiceError;
|
||||
pub use types::ApprovalsReviewer;
|
||||
|
||||
pub use codex_git::GhostSnapshotConfig;
|
||||
pub use codex_git_utils::GhostSnapshotConfig;
|
||||
|
||||
/// Maximum number of bytes of the documentation that will be embedded. Larger
|
||||
/// files are *silently truncated* to this size so we do not take up too much of
|
||||
|
||||
@@ -7,10 +7,10 @@ mod tests;
|
||||
|
||||
use crate::config::ConfigToml;
|
||||
use crate::config_loader::layer_io::LoadedConfigLayers;
|
||||
use crate::git_info::resolve_root_git_project_for_trust;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_config::CONFIG_TOML_FILE;
|
||||
use codex_config::ConfigRequirementsWithSources;
|
||||
use codex_git_utils::resolve_root_git_project_for_trust;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::truncate;
|
||||
use crate::truncate::TruncationPolicy;
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
|
||||
use codex_git::GhostCommit;
|
||||
use codex_git_utils::GhostCommit;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::models::BaseInstructions;
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
use super::*;
|
||||
|
||||
use codex_git_utils::GitInfo;
|
||||
use codex_git_utils::GitSha;
|
||||
use codex_git_utils::collect_git_info;
|
||||
use codex_git_utils::get_has_changes;
|
||||
use codex_git_utils::git_diff_to_remote;
|
||||
use codex_git_utils::recent_commits;
|
||||
use codex_git_utils::resolve_root_git_project_for_trust;
|
||||
use core_test_support::skip_if_sandbox;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
use tokio::process::Command;
|
||||
|
||||
// Helper function to create a test git repository
|
||||
async fn create_test_git_repo(temp_dir: &TempDir) -> PathBuf {
|
||||
@@ -191,7 +197,7 @@ async fn test_collect_git_info_git_repository() {
|
||||
|
||||
// Should have commit hash
|
||||
assert!(git_info.commit_hash.is_some());
|
||||
let commit_hash = git_info.commit_hash.unwrap();
|
||||
let commit_hash = git_info.commit_hash.unwrap().0;
|
||||
assert_eq!(commit_hash.len(), 40); // SHA-1 hash should be 40 characters
|
||||
assert!(commit_hash.chars().all(|c| c.is_ascii_hexdigit()));
|
||||
|
||||
@@ -558,7 +564,7 @@ async fn test_get_git_working_tree_state_unpushed_commit() {
|
||||
#[test]
|
||||
fn test_git_info_serialization() {
|
||||
let git_info = GitInfo {
|
||||
commit_hash: Some("abc123def456".to_string()),
|
||||
commit_hash: Some(GitSha::new("abc123def456")),
|
||||
branch: Some("main".to_string()),
|
||||
repository_url: Some("https://github.com/example/repo.git".to_string()),
|
||||
};
|
||||
|
||||
@@ -41,7 +41,8 @@ mod exec_policy;
|
||||
pub mod external_agent_config;
|
||||
pub mod file_watcher;
|
||||
mod flags;
|
||||
pub mod git_info;
|
||||
#[cfg(test)]
|
||||
mod git_info_tests;
|
||||
mod guardian;
|
||||
mod hook_runtime;
|
||||
pub mod instructions;
|
||||
|
||||
@@ -2,9 +2,9 @@ use super::PluginManifestInterface;
|
||||
use super::load_plugin_manifest;
|
||||
use super::store::PluginId;
|
||||
use super::store::PluginIdError;
|
||||
use crate::git_info::get_git_repo_root;
|
||||
use codex_app_server_protocol::PluginAuthPolicy;
|
||||
use codex_app_server_protocol::PluginInstallPolicy;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_protocol::protocol::Product;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use dirs::home_dir;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::codex::Session;
|
||||
use crate::compact::content_items_to_text;
|
||||
use crate::event_mapping::is_contextual_user_message_content;
|
||||
use crate::git_info::resolve_root_git_project_for_trust;
|
||||
use crate::truncate::TruncationPolicy;
|
||||
use crate::truncate::truncate_text;
|
||||
use chrono::Utc;
|
||||
use codex_git_utils::resolve_root_git_project_for_trust;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_state::SortKey;
|
||||
use codex_state::ThreadMetadata;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use codex_git::merge_base_with_head;
|
||||
use codex_git_utils::merge_base_with_head;
|
||||
use codex_protocol::protocol::ReviewRequest;
|
||||
use codex_protocol::protocol::ReviewTarget;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -1039,7 +1039,7 @@ async fn read_head_summary(path: &Path, head_limit: usize) -> io::Result<HeadTai
|
||||
summary.git_sha = session_meta_line
|
||||
.git
|
||||
.as_ref()
|
||||
.and_then(|git| git.commit_hash.clone());
|
||||
.and_then(|git| git.commit_hash.as_ref().map(|sha| sha.0.clone()));
|
||||
summary.git_origin_url = session_meta_line
|
||||
.git
|
||||
.as_ref()
|
||||
|
||||
@@ -55,7 +55,7 @@ pub(crate) fn builder_from_session_meta(
|
||||
builder.sandbox_policy = SandboxPolicy::new_read_only_policy();
|
||||
builder.approval_mode = AskForApproval::OnRequest;
|
||||
if let Some(git) = session_meta.git.as_ref() {
|
||||
builder.git_sha = git.commit_hash.clone();
|
||||
builder.git_sha = git.commit_hash.as_ref().map(|sha| sha.0.clone());
|
||||
builder.git_branch = git.branch.clone();
|
||||
builder.git_origin_url = git.repository_url.clone();
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ async fn backfill_sessions_preserves_existing_git_branch_and_fills_missing_git_f
|
||||
"2026-01-27T12:34:56Z",
|
||||
thread_uuid,
|
||||
Some(GitInfo {
|
||||
commit_hash: Some("rollout-sha".to_string()),
|
||||
commit_hash: Some(codex_git_utils::GitSha::new("rollout-sha")),
|
||||
branch: Some("rollout-branch".to_string()),
|
||||
repository_url: Some("git@example.com:openai/codex.git".to_string()),
|
||||
}),
|
||||
|
||||
@@ -41,13 +41,14 @@ use super::policy::EventPersistenceMode;
|
||||
use super::policy::is_persisted_response_item;
|
||||
use crate::config::Config;
|
||||
use crate::default_client::originator;
|
||||
use crate::git_info::collect_git_info;
|
||||
use crate::path_utils;
|
||||
use crate::state_db;
|
||||
use crate::state_db::StateDbHandle;
|
||||
use crate::truncate::TruncationPolicy;
|
||||
use crate::truncate::truncate_text;
|
||||
use codex_git_utils::collect_git_info;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::GitInfo as ProtocolGitInfo;
|
||||
use codex_protocol::protocol::InitialHistory;
|
||||
use codex_protocol::protocol::ResumedHistory;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
@@ -846,7 +847,11 @@ async fn write_session_meta(
|
||||
default_provider: &str,
|
||||
generate_memories: bool,
|
||||
) -> std::io::Result<()> {
|
||||
let git_info = collect_git_info(cwd).await;
|
||||
let git_info = collect_git_info(cwd).await.map(|info| ProtocolGitInfo {
|
||||
commit_hash: info.commit_hash,
|
||||
branch: info.branch,
|
||||
repository_url: info.repository_url,
|
||||
});
|
||||
let session_meta_line = SessionMetaLine {
|
||||
meta: session_meta,
|
||||
git: git_info,
|
||||
|
||||
@@ -5,10 +5,10 @@ use crate::state::TaskKind;
|
||||
use crate::tasks::SessionTask;
|
||||
use crate::tasks::SessionTaskContext;
|
||||
use async_trait::async_trait;
|
||||
use codex_git::CreateGhostCommitOptions;
|
||||
use codex_git::GhostSnapshotReport;
|
||||
use codex_git::GitToolingError;
|
||||
use codex_git::create_ghost_commit_with_report;
|
||||
use codex_git_utils::CreateGhostCommitOptions;
|
||||
use codex_git_utils::GhostSnapshotReport;
|
||||
use codex_git_utils::GitToolingError;
|
||||
use codex_git_utils::create_ghost_commit_with_report;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use codex_utils_readiness::Readiness;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use codex_git::LargeUntrackedDir;
|
||||
use codex_git_utils::LargeUntrackedDir;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ use crate::state::TaskKind;
|
||||
use crate::tasks::SessionTask;
|
||||
use crate::tasks::SessionTaskContext;
|
||||
use async_trait::async_trait;
|
||||
use codex_git::RestoreGhostCommitOptions;
|
||||
use codex_git::restore_ghost_commit_with_options;
|
||||
use codex_git_utils::RestoreGhostCommitOptions;
|
||||
use codex_git_utils::restore_ghost_commit_with_options;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
@@ -8,11 +8,11 @@ use std::sync::RwLock;
|
||||
use serde::Serialize;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use crate::git_info::get_git_remote_urls_assume_git_repo;
|
||||
use crate::git_info::get_git_repo_root;
|
||||
use crate::git_info::get_has_changes;
|
||||
use crate::git_info::get_head_commit_hash;
|
||||
use crate::sandbox_tags::sandbox_tag;
|
||||
use codex_git_utils::get_git_remote_urls_assume_git_repo;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_git_utils::get_has_changes;
|
||||
use codex_git_utils::get_head_commit_hash;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
|
||||
@@ -94,11 +94,12 @@ fn build_turn_metadata_bag(
|
||||
pub async fn build_turn_metadata_header(cwd: &Path, sandbox: Option<&str>) -> Option<String> {
|
||||
let repo_root = get_git_repo_root(cwd).map(|root| root.to_string_lossy().into_owned());
|
||||
|
||||
let (latest_git_commit_hash, associated_remote_urls, has_changes) = tokio::join!(
|
||||
let (head_commit_hash, associated_remote_urls, has_changes) = tokio::join!(
|
||||
get_head_commit_hash(cwd),
|
||||
get_git_remote_urls_assume_git_repo(cwd),
|
||||
get_has_changes(cwd),
|
||||
);
|
||||
let latest_git_commit_hash = head_commit_hash.map(|sha| sha.0);
|
||||
if latest_git_commit_hash.is_none()
|
||||
&& associated_remote_urls.is_none()
|
||||
&& has_changes.is_none()
|
||||
@@ -231,11 +232,12 @@ impl TurnMetadataState {
|
||||
}
|
||||
|
||||
async fn fetch_workspace_git_metadata(&self) -> WorkspaceGitMetadata {
|
||||
let (latest_git_commit_hash, associated_remote_urls, has_changes) = tokio::join!(
|
||||
let (head_commit_hash, associated_remote_urls, has_changes) = tokio::join!(
|
||||
get_head_commit_hash(&self.cwd),
|
||||
get_git_remote_urls_assume_git_repo(&self.cwd),
|
||||
get_has_changes(&self.cwd),
|
||||
);
|
||||
let latest_git_commit_hash = head_commit_hash.map(|sha| sha.0);
|
||||
|
||||
WorkspaceGitMetadata {
|
||||
associated_remote_urls,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use assert_cmd::Command as AssertCommand;
|
||||
use codex_core::auth::CODEX_API_KEY_ENV_VAR;
|
||||
use codex_git_utils::collect_git_info;
|
||||
use codex_protocol::protocol::GitInfo;
|
||||
use codex_utils_cargo_bin::find_resource;
|
||||
use core_test_support::fs_wait;
|
||||
@@ -596,7 +597,7 @@ async fn integration_git_info_unit_test() {
|
||||
.unwrap();
|
||||
|
||||
// 3. Test git info collection directly
|
||||
let git_info = codex_core::git_info::collect_git_info(&git_repo).await;
|
||||
let git_info = collect_git_info(&git_repo).await;
|
||||
|
||||
// 4. Verify git info is present and contains expected data
|
||||
assert!(git_info.is_some(), "Git info should be collected");
|
||||
@@ -608,7 +609,7 @@ async fn integration_git_info_unit_test() {
|
||||
git_info.commit_hash.is_some(),
|
||||
"Git info should contain commit_hash"
|
||||
);
|
||||
let commit_hash = git_info.commit_hash.as_ref().unwrap();
|
||||
let commit_hash = &git_info.commit_hash.as_ref().unwrap().0;
|
||||
assert_eq!(commit_hash.len(), 40, "Commit hash should be 40 characters");
|
||||
assert!(
|
||||
commit_hash.chars().all(|c| c.is_ascii_hexdigit()),
|
||||
|
||||
@@ -29,6 +29,7 @@ codex-app-server-protocol = { workspace = true }
|
||||
codex-cloud-requirements = { workspace = true }
|
||||
codex-core = { workspace = true }
|
||||
codex-feedback = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-otel = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
|
||||
@@ -64,9 +64,9 @@ use codex_core::config_loader::ConfigLoadError;
|
||||
use codex_core::config_loader::LoaderOverrides;
|
||||
use codex_core::config_loader::format_config_error_with_source;
|
||||
use codex_core::format_exec_policy_error_with_source;
|
||||
use codex_core::git_info::get_git_repo_root;
|
||||
use codex_core::path_utils;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_otel::set_parent_from_context;
|
||||
use codex_otel::traceparent_context_from_env;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
load("//:defs.bzl", "codex_rust_crate")
|
||||
|
||||
codex_rust_crate(
|
||||
name = "git",
|
||||
crate_name = "codex_git",
|
||||
name = "git-utils",
|
||||
crate_name = "codex_git_utils",
|
||||
)
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "codex-git"
|
||||
name = "codex-git-utils"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
@@ -9,12 +9,15 @@ readme = "README.md"
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
futures = { workspace = true, features = ["alloc"] }
|
||||
once_cell = { workspace = true }
|
||||
regex = "1"
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tempfile = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["macros", "process", "rt", "time"] }
|
||||
ts-rs = { workspace = true, features = [
|
||||
"uuid-impl",
|
||||
"serde-json-impl",
|
||||
@@ -1,4 +1,4 @@
|
||||
# codex-git
|
||||
# codex-git-utils
|
||||
|
||||
Helpers for interacting with git, including patch application and worktree
|
||||
snapshot utilities.
|
||||
@@ -6,7 +6,7 @@ snapshot utilities.
|
||||
```rust,no_run
|
||||
use std::path::Path;
|
||||
|
||||
use codex_git::{
|
||||
use codex_git_utils::{
|
||||
apply_git_patch, create_ghost_commit, restore_ghost_commit, ApplyGitRequest,
|
||||
CreateGhostCommitOptions,
|
||||
};
|
||||
@@ -4,15 +4,17 @@ use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::util::resolve_path;
|
||||
use codex_app_server_protocol::GitSha;
|
||||
use codex_protocol::protocol::GitInfo;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use futures::future::join_all;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use tokio::process::Command;
|
||||
use tokio::time::Duration as TokioDuration;
|
||||
use tokio::time::timeout;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::GitSha;
|
||||
|
||||
/// Return `true` if the project folder specified by the `Config` is inside a
|
||||
/// Git repository.
|
||||
@@ -38,6 +40,19 @@ pub fn get_git_repo_root(base_dir: &Path) -> Option<PathBuf> {
|
||||
/// Timeout for git commands to prevent freezing on large repositories
|
||||
const GIT_COMMAND_TIMEOUT: TokioDuration = TokioDuration::from_secs(5);
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, TS)]
|
||||
pub struct GitInfo {
|
||||
/// Current commit hash (SHA)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub commit_hash: Option<GitSha>,
|
||||
/// Current branch name
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub branch: Option<String>,
|
||||
/// Repository URL (if available from remote)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub repository_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct GitDiffToRemote {
|
||||
pub sha: GitSha,
|
||||
@@ -77,7 +92,7 @@ pub async fn collect_git_info(cwd: &Path) -> Option<GitInfo> {
|
||||
&& output.status.success()
|
||||
&& let Ok(hash) = String::from_utf8(output.stdout)
|
||||
{
|
||||
git_info.commit_hash = Some(hash.trim().to_string());
|
||||
git_info.commit_hash = Some(GitSha::new(hash.trim()));
|
||||
}
|
||||
|
||||
// Process branch name
|
||||
@@ -127,7 +142,7 @@ pub async fn get_git_remote_urls_assume_git_repo(cwd: &Path) -> Option<BTreeMap<
|
||||
}
|
||||
|
||||
/// Return the current HEAD commit hash without checking whether `cwd` is in a git repo.
|
||||
pub async fn get_head_commit_hash(cwd: &Path) -> Option<String> {
|
||||
pub async fn get_head_commit_hash(cwd: &Path) -> Option<GitSha> {
|
||||
let output = run_git_command_with_timeout(&["rev-parse", "HEAD"], cwd).await?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
@@ -138,7 +153,7 @@ pub async fn get_head_commit_hash(cwd: &Path) -> Option<String> {
|
||||
if hash.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(hash.to_string())
|
||||
Some(GitSha::new(hash))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,7 +631,11 @@ pub fn resolve_root_git_project_for_trust(cwd: &Path) -> Option<PathBuf> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let git_dir_path = canonicalize_or_raw(resolve_path(&repo_root, &PathBuf::from(git_dir_rel)));
|
||||
let git_dir_path = canonicalize_or_raw(
|
||||
AbsolutePathBuf::resolve_path_against_base(git_dir_rel, &repo_root)
|
||||
.ok()?
|
||||
.into_path_buf(),
|
||||
);
|
||||
let worktrees_dir = git_dir_path.parent()?;
|
||||
if worktrees_dir.file_name() != Some(OsStr::new("worktrees")) {
|
||||
return None;
|
||||
@@ -689,7 +708,3 @@ pub async fn current_branch_name(cwd: &Path) -> Option<String> {
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|name| !name.is_empty())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "git_info_tests.rs"]
|
||||
mod tests;
|
||||
@@ -5,6 +5,7 @@ mod apply;
|
||||
mod branch;
|
||||
mod errors;
|
||||
mod ghost_commits;
|
||||
mod info;
|
||||
mod operations;
|
||||
mod platform;
|
||||
|
||||
@@ -28,6 +29,21 @@ pub use ghost_commits::create_ghost_commit_with_report;
|
||||
pub use ghost_commits::restore_ghost_commit;
|
||||
pub use ghost_commits::restore_ghost_commit_with_options;
|
||||
pub use ghost_commits::restore_to_commit;
|
||||
pub use info::CommitLogEntry;
|
||||
pub use info::GitDiffToRemote;
|
||||
pub use info::GitInfo;
|
||||
pub use info::collect_git_info;
|
||||
pub use info::current_branch_name;
|
||||
pub use info::default_branch_name;
|
||||
pub use info::get_git_remote_urls;
|
||||
pub use info::get_git_remote_urls_assume_git_repo;
|
||||
pub use info::get_git_repo_root;
|
||||
pub use info::get_has_changes;
|
||||
pub use info::get_head_commit_hash;
|
||||
pub use info::git_diff_to_remote;
|
||||
pub use info::local_git_branches;
|
||||
pub use info::recent_commits;
|
||||
pub use info::resolve_root_git_project_for_trust;
|
||||
pub use platform::create_symlink;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@@ -36,6 +52,17 @@ use ts_rs::TS;
|
||||
|
||||
type CommitID = String;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, TS)]
|
||||
#[serde(transparent)]
|
||||
#[ts(type = "string")]
|
||||
pub struct GitSha(pub String);
|
||||
|
||||
impl GitSha {
|
||||
pub fn new(sha: &str) -> Self {
|
||||
Self(sha.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Details of a ghost commit created from a repository state.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
|
||||
pub struct GhostCommit {
|
||||
@@ -13,7 +13,7 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
codex-execpolicy = { workspace = true }
|
||||
codex-git = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
codex-utils-image = { workspace = true }
|
||||
icu_decimal = { workspace = true }
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::protocol::SandboxPolicy;
|
||||
use crate::protocol::WritableRoot;
|
||||
use crate::user_input::UserInput;
|
||||
use codex_execpolicy::Policy;
|
||||
use codex_git::GhostCommit;
|
||||
use codex_git_utils::GhostCommit;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_image::error::ImageProcessingError;
|
||||
use schemars::JsonSchema;
|
||||
|
||||
@@ -49,6 +49,7 @@ use crate::request_permissions::RequestPermissionsEvent;
|
||||
use crate::request_permissions::RequestPermissionsResponse;
|
||||
use crate::request_user_input::RequestUserInputResponse;
|
||||
use crate::user_input::UserInput;
|
||||
use codex_git_utils::GitSha;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@@ -2639,7 +2640,7 @@ pub struct RolloutLine {
|
||||
pub struct GitInfo {
|
||||
/// Current commit hash (SHA)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub commit_hash: Option<String>,
|
||||
pub commit_hash: Option<GitSha>,
|
||||
/// Current branch name
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub branch: Option<String>,
|
||||
|
||||
@@ -11,6 +11,7 @@ workspace = true
|
||||
age = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-keyring-store = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_keyring_store::DefaultKeyringStore;
|
||||
use codex_keyring_store::KeyringStore;
|
||||
use schemars::JsonSchema;
|
||||
@@ -161,22 +162,6 @@ pub fn environment_id_from_cwd(cwd: &Path) -> String {
|
||||
format!("cwd-{short}")
|
||||
}
|
||||
|
||||
fn get_git_repo_root(base_dir: &Path) -> Option<PathBuf> {
|
||||
let mut dir = base_dir.to_path_buf();
|
||||
|
||||
loop {
|
||||
if dir.join(".git").exists() {
|
||||
return Some(dir);
|
||||
}
|
||||
|
||||
if !dir.pop() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn compute_keyring_account(codex_home: &Path) -> String {
|
||||
let canonical = codex_home
|
||||
.canonicalize()
|
||||
|
||||
@@ -22,6 +22,7 @@ tracing-subscriber = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
codex-git-utils = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -61,7 +61,7 @@ fn apply_session_meta_from_item(metadata: &mut ThreadMetadata, meta_line: &Sessi
|
||||
metadata.cwd = meta_line.meta.cwd.clone();
|
||||
}
|
||||
if let Some(git) = meta_line.git.as_ref() {
|
||||
metadata.git_sha = git.commit_hash.clone();
|
||||
metadata.git_sha = git.commit_hash.as_ref().map(|sha| sha.0.clone());
|
||||
metadata.git_branch = git.branch.clone();
|
||||
metadata.git_origin_url = git.repository_url.clone();
|
||||
}
|
||||
|
||||
@@ -1089,7 +1089,7 @@ mod tests {
|
||||
memory_mode: None,
|
||||
},
|
||||
git: Some(GitInfo {
|
||||
commit_hash: Some("rollout-sha".to_string()),
|
||||
commit_hash: Some(codex_git_utils::GitSha::new("rollout-sha")),
|
||||
branch: Some("rollout-branch".to_string()),
|
||||
repository_url: Some("git@example.com:openai/codex.git".to_string()),
|
||||
}),
|
||||
|
||||
@@ -40,6 +40,7 @@ codex-core = { workspace = true }
|
||||
codex-features = { workspace = true }
|
||||
codex-feedback = { workspace = true }
|
||||
codex-file-search = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-login = { workspace = true }
|
||||
codex-otel = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
|
||||
@@ -69,9 +69,6 @@ use codex_core::config::types::Notifications;
|
||||
use codex_core::config::types::WindowsSandboxModeToml;
|
||||
use codex_core::config_loader::ConfigLayerStackOrdering;
|
||||
use codex_core::find_thread_name_by_id;
|
||||
use codex_core::git_info::current_branch_name;
|
||||
use codex_core::git_info::get_git_repo_root;
|
||||
use codex_core::git_info::local_git_branches;
|
||||
use codex_core::mcp::McpManager;
|
||||
use codex_core::models_manager::manager::ModelsManager;
|
||||
use codex_core::plugins::PluginsManager;
|
||||
@@ -81,6 +78,12 @@ use codex_core::skills::model::SkillMetadata;
|
||||
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_features::FEATURES;
|
||||
use codex_features::Feature;
|
||||
#[cfg(test)]
|
||||
use codex_git_utils::CommitLogEntry;
|
||||
use codex_git_utils::current_branch_name;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_git_utils::local_git_branches;
|
||||
use codex_git_utils::recent_commits;
|
||||
use codex_otel::RuntimeMetricsSummary;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_protocol::ThreadId;
|
||||
@@ -9246,7 +9249,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
pub(crate) async fn show_review_commit_picker(&mut self, cwd: &Path) {
|
||||
let commits = codex_core::git_info::recent_commits(cwd, /*limit*/ 100).await;
|
||||
let commits = recent_commits(cwd, /*limit*/ 100).await;
|
||||
|
||||
let mut items: Vec<SelectionItem> = Vec::with_capacity(commits.len());
|
||||
for entry in commits {
|
||||
@@ -9648,7 +9651,7 @@ async fn fetch_rate_limits(base_url: String, auth: CodexAuth) -> Vec<RateLimitSn
|
||||
#[cfg(test)]
|
||||
pub(crate) fn show_review_commit_picker_with_entries(
|
||||
chat: &mut ChatWidget,
|
||||
entries: Vec<codex_core::git_info::CommitLogEntry>,
|
||||
entries: Vec<CommitLogEntry>,
|
||||
) {
|
||||
let mut items: Vec<SelectionItem> = Vec::with_capacity(entries.len());
|
||||
for entry in entries {
|
||||
|
||||
@@ -51,6 +51,7 @@ use codex_core::plugins::OPENAI_CURATED_MARKETPLACE_NAME;
|
||||
use codex_core::skills::model::SkillMetadata;
|
||||
use codex_features::FEATURES;
|
||||
use codex_features::Feature;
|
||||
use codex_git_utils::CommitLogEntry;
|
||||
use codex_otel::RuntimeMetricsSummary;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_protocol::ThreadId;
|
||||
@@ -6725,12 +6726,12 @@ async fn review_commit_picker_shows_subjects_without_timestamps() {
|
||||
|
||||
// Show commit picker with synthetic entries.
|
||||
let entries = vec![
|
||||
codex_core::git_info::CommitLogEntry {
|
||||
CommitLogEntry {
|
||||
sha: "1111111deadbeef".to_string(),
|
||||
timestamp: 0,
|
||||
subject: "Add new feature X".to_string(),
|
||||
},
|
||||
codex_core::git_info::CommitLogEntry {
|
||||
CommitLogEntry {
|
||||
sha: "2222222cafebabe".to_string(),
|
||||
timestamp: 0,
|
||||
subject: "Fix bug Y".to_string(),
|
||||
|
||||
@@ -92,7 +92,7 @@ use crate::terminal_palette::default_bg;
|
||||
use crate::terminal_palette::indexed_color;
|
||||
use crate::terminal_palette::rgb_color;
|
||||
use crate::terminal_palette::stdout_color_level;
|
||||
use codex_core::git_info::get_git_repo_root;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
use codex_terminal_detection::TerminalName;
|
||||
use codex_terminal_detection::terminal_info;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_core::config::set_project_trust_level;
|
||||
use codex_core::git_info::resolve_root_git_project_for_trust;
|
||||
use codex_git_utils::resolve_root_git_project_for_trust;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
|
||||
@@ -44,6 +44,7 @@ codex-core = { workspace = true }
|
||||
codex-features = { workspace = true }
|
||||
codex-feedback = { workspace = true }
|
||||
codex-file-search = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-login = { workspace = true }
|
||||
codex-otel = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
|
||||
@@ -89,9 +89,6 @@ use codex_core::config::types::Notifications;
|
||||
use codex_core::config::types::WindowsSandboxModeToml;
|
||||
use codex_core::config_loader::ConfigLayerStackOrdering;
|
||||
use codex_core::find_thread_name_by_id;
|
||||
use codex_core::git_info::current_branch_name;
|
||||
use codex_core::git_info::get_git_repo_root;
|
||||
use codex_core::git_info::local_git_branches;
|
||||
use codex_core::plugins::PluginsManager;
|
||||
use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME;
|
||||
use codex_core::skills::model::SkillMetadata;
|
||||
@@ -99,6 +96,12 @@ use codex_core::skills::model::SkillMetadata;
|
||||
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_features::FEATURES;
|
||||
use codex_features::Feature;
|
||||
#[cfg(test)]
|
||||
use codex_git_utils::CommitLogEntry;
|
||||
use codex_git_utils::current_branch_name;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_git_utils::local_git_branches;
|
||||
use codex_git_utils::recent_commits;
|
||||
use codex_otel::RuntimeMetricsSummary;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_protocol::ThreadId;
|
||||
@@ -10414,7 +10417,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
pub(crate) async fn show_review_commit_picker(&mut self, cwd: &Path) {
|
||||
let commits = codex_core::git_info::recent_commits(cwd, /*limit*/ 100).await;
|
||||
let commits = recent_commits(cwd, /*limit*/ 100).await;
|
||||
|
||||
let mut items: Vec<SelectionItem> = Vec::with_capacity(commits.len());
|
||||
for entry in commits {
|
||||
@@ -10796,7 +10799,7 @@ fn hook_event_label(event_name: codex_protocol::protocol::HookEventName) -> &'st
|
||||
#[cfg(test)]
|
||||
pub(crate) fn show_review_commit_picker_with_entries(
|
||||
chat: &mut ChatWidget,
|
||||
entries: Vec<codex_core::git_info::CommitLogEntry>,
|
||||
entries: Vec<CommitLogEntry>,
|
||||
) {
|
||||
let mut items: Vec<SelectionItem> = Vec::with_capacity(entries.len());
|
||||
for entry in entries {
|
||||
|
||||
@@ -74,6 +74,7 @@ use codex_core::plugins::OPENAI_CURATED_MARKETPLACE_NAME;
|
||||
use codex_core::skills::model::SkillMetadata;
|
||||
use codex_features::FEATURES;
|
||||
use codex_features::Feature;
|
||||
use codex_git_utils::CommitLogEntry;
|
||||
use codex_otel::RuntimeMetricsSummary;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_protocol::ThreadId;
|
||||
@@ -7322,12 +7323,12 @@ async fn review_commit_picker_shows_subjects_without_timestamps() {
|
||||
|
||||
// Show commit picker with synthetic entries.
|
||||
let entries = vec![
|
||||
codex_core::git_info::CommitLogEntry {
|
||||
CommitLogEntry {
|
||||
sha: "1111111deadbeef".to_string(),
|
||||
timestamp: 0,
|
||||
subject: "Add new feature X".to_string(),
|
||||
},
|
||||
codex_core::git_info::CommitLogEntry {
|
||||
CommitLogEntry {
|
||||
sha: "2222222cafebabe".to_string(),
|
||||
timestamp: 0,
|
||||
subject: "Fix bug Y".to_string(),
|
||||
|
||||
@@ -92,7 +92,7 @@ use crate::terminal_palette::default_bg;
|
||||
use crate::terminal_palette::indexed_color;
|
||||
use crate::terminal_palette::rgb_color;
|
||||
use crate::terminal_palette::stdout_color_level;
|
||||
use codex_core::git_info::get_git_repo_root;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
use codex_terminal_detection::TerminalName;
|
||||
use codex_terminal_detection::terminal_info;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_core::config::set_project_trust_level;
|
||||
use codex_core::git_info::resolve_root_git_project_for_trust;
|
||||
use codex_git_utils::resolve_root_git_project_for_trust;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
|
||||
Reference in New Issue
Block a user