Files
codex/prs/bolinfest/PR-2142.md
2025-09-02 15:17:45 -07:00

9.6 KiB

PR #2142: add branch name w/ sync function for git info

Description

image

Full Diff

diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs
index 2dd48bf366..024fec1fae 100644
--- a/codex-rs/core/src/codex.rs
+++ b/codex-rs/core/src/codex.rs
@@ -902,9 +902,11 @@ async fn submission_loop(
                     }
                 }
 
-                // Gather history metadata for SessionConfiguredEvent.
-                let (history_log_id, history_entry_count) =
-                    crate::message_history::history_metadata(&config).await;
+                // Gather history metadata for SessionConfiguredEvent and await git info.
+                let git_info_fut = crate::git_info::collect_git_info(&sess.as_ref().unwrap().cwd);
+                let history_fut = crate::message_history::history_metadata(&config);
+                let (git_info, (history_log_id, history_entry_count)) =
+                    tokio::join!(git_info_fut, history_fut);
 
                 // ack
                 let events = std::iter::once(Event {
@@ -914,6 +916,7 @@ async fn submission_loop(
                         model,
                         history_log_id,
                         history_entry_count,
+                        git_info,
                     }),
                 })
                 .chain(mcp_connection_errors.into_iter());
diff --git a/codex-rs/core/src/git_info.rs b/codex-rs/core/src/git_info.rs
index 52d029f669..506b02b76c 100644
--- a/codex-rs/core/src/git_info.rs
+++ b/codex-rs/core/src/git_info.rs
@@ -6,8 +6,12 @@ use tokio::process::Command;
 use tokio::time::Duration as TokioDuration;
 use tokio::time::timeout;
 
-/// Timeout for git commands to prevent freezing on large repositories
-const GIT_COMMAND_TIMEOUT: TokioDuration = TokioDuration::from_secs(5);
+/// Timeout for git commands to prevent freezing on large repositories.
+///
+/// Tests that wait for the initial `SessionConfigured` event use a short
+/// timeout (~1s). Collecting Git info is best-effort and must not block the
+/// session handshake, so we cap individual Git calls to a small value.
+const GIT_COMMAND_TIMEOUT: TokioDuration = TokioDuration::from_millis(400);
 
 #[derive(Serialize, Deserialize, Clone, Debug)]
 pub struct GitInfo {
diff --git a/codex-rs/core/src/protocol.rs b/codex-rs/core/src/protocol.rs
index 4972f10d98..5db3cd7c48 100644
--- a/codex-rs/core/src/protocol.rs
+++ b/codex-rs/core/src/protocol.rs
@@ -19,6 +19,7 @@ use uuid::Uuid;
 
 use crate::config_types::ReasoningEffort as ReasoningEffortConfig;
 use crate::config_types::ReasoningSummary as ReasoningSummaryConfig;
+use crate::git_info::GitInfo;
 use crate::message_history::HistoryEntry;
 use crate::model_provider_info::ModelProviderInfo;
 use crate::parse_command::ParsedCommand;
@@ -695,6 +696,10 @@ pub struct SessionConfiguredEvent {
 
     /// Current number of entries in the history log.
     pub history_entry_count: usize,
+
+    /// Optional Git metadata for the configured cwd.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub git_info: Option<GitInfo>,
 }
 
 /// User's decision in response to an ExecApprovalRequest.
@@ -757,6 +762,7 @@ mod tests {
                 model: "codex-mini-latest".to_string(),
                 history_log_id: 0,
                 history_entry_count: 0,
+                git_info: None,
             }),
         };
         let serialized = serde_json::to_string(&event).unwrap();
diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs
index 1d35dcb73f..aca6a75592 100644
--- a/codex-rs/exec/src/event_processor_with_human_output.rs
+++ b/codex-rs/exec/src/event_processor_with_human_output.rs
@@ -494,6 +494,7 @@ impl EventProcessor for EventProcessorWithHumanOutput {
                     model,
                     history_log_id: _,
                     history_entry_count: _,
+                    git_info: _,
                 } = session_configured_event;
 
                 ts_println!(
diff --git a/codex-rs/mcp-server/src/mcp_protocol.rs b/codex-rs/mcp-server/src/mcp_protocol.rs
index 0528e18a39..426352e647 100644
--- a/codex-rs/mcp-server/src/mcp_protocol.rs
+++ b/codex-rs/mcp-server/src/mcp_protocol.rs
@@ -908,6 +908,7 @@ mod tests {
                 model: "codex-mini-latest".into(),
                 history_log_id: 42,
                 history_entry_count: 3,
+                git_info: None,
             }),
         };
 
diff --git a/codex-rs/mcp-server/src/outgoing_message.rs b/codex-rs/mcp-server/src/outgoing_message.rs
index e7b0b9b63c..9528e35fbf 100644
--- a/codex-rs/mcp-server/src/outgoing_message.rs
+++ b/codex-rs/mcp-server/src/outgoing_message.rs
@@ -244,6 +244,7 @@ mod tests {
                 model: "gpt-4o".to_string(),
                 history_log_id: 1,
                 history_entry_count: 1000,
+                git_info: None,
             }),
         };
 
@@ -284,6 +285,7 @@ mod tests {
             model: "gpt-4o".to_string(),
             history_log_id: 1,
             history_entry_count: 1000,
+            git_info: None,
         };
         let event = Event {
             id: "1".to_string(),
diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs
index b49e59972c..97a1dd865a 100644
--- a/codex-rs/tui/src/history_cell.rs
+++ b/codex-rs/tui/src/history_cell.rs
@@ -233,6 +233,7 @@ impl HistoryCell {
             session_id: _,
             history_log_id: _,
             history_entry_count: _,
+            git_info,
         } = event;
         if is_first_event {
             let cwd_str = match relativize_to_home(&config.cwd) {
@@ -241,6 +242,18 @@ impl HistoryCell {
                 None => config.cwd.display().to_string(),
             };
 
+            // Use async-collected Git info from the event if available.
+            let branch_suffix = git_info
+                .and_then(|g| g.branch)
+                .map(|b| format!(" ({b})"))
+                .unwrap_or_default();
+
+            let path_and_branch = if branch_suffix.is_empty() {
+                format!(" {cwd_str}")
+            } else {
+                format!(" {cwd_str}{branch_suffix}")
+            };
+
             let lines: Vec<Line<'static>> = vec![
                 Line::from(vec![
                     Span::raw(">_ ").dim(),
@@ -248,7 +261,7 @@ impl HistoryCell {
                         "You are using OpenAI Codex in",
                         Style::default().add_modifier(Modifier::BOLD),
                     ),
-                    Span::raw(format!(" {cwd_str}")).dim(),
+                    Span::raw(path_and_branch).dim(),
                 ]),
                 Line::from("".dim()),
                 Line::from(" To get started, describe a task or try one of these commands:".dim()),
@@ -883,6 +896,8 @@ impl HistoryCell {
     }
 }
 
+//
+
 impl WidgetRef for &HistoryCell {
     fn render_ref(&self, area: Rect, buf: &mut Buffer) {
         Paragraph::new(Text::from(self.plain_lines()))

Review Comments

codex-rs/core/src/codex.rs

@@ -902,9 +902,11 @@ async fn submission_loop(
                     }
                 }
 
-                // Gather history metadata for SessionConfiguredEvent.
-                let (history_log_id, history_entry_count) =
-                    crate::message_history::history_metadata(&config).await;
+                // Gather history metadata for SessionConfiguredEvent and await git info.

I feel like we should be specifying the timeout somehow at this callsite (either updating collect_git_info() to take a timeout, or wrapping these calls with tokio::time::timeout) to ensure that this runs in a bounded amount of time.

One challenge is that here, we are latency-sensitive and I agre with the short timeout.

Though the other callsite in rollout.rs is less latency-sensitive, and arguably should be able to specify a more generous timeout.

That said, from the callsite, one would expect to specify the timeout of the overall operation, whereas internally inside collect_git_info(), run_git_command_with_timeout() is run twice in series, so if collect_git_info() took its own timeout argument, arguably we should divide it by two and use the halved version for each internal call to run_git_command_with_timeout()?

I admit there's already the existing issue here that crate::message_history::history_metadata() should also be bound by a timeout, though I don't know what value to use (0?) if the timeout expires before it completes.

codex-rs/tui/src/history_cell.rs

@@ -212,14 +212,27 @@ impl HistoryCell {
                 None => config.cwd.display().to_string(),
             };
 
+            // Determine the current Git branch (if any) for display using
+            // the shared Git info collector in a way that is safe in sync contexts.
+            let branch_suffix = codex_core::git_info::collect_git_info_blocking(&config.cwd)

Can we make this part of the SessionConfiguredEvent so we can do all this work async?

@@ -883,6 +896,8 @@ impl HistoryCell {
     }
 }
 
+//

remove?