Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ harness = false
# cargo's OPT_LEVEL), and nearly every test creates databases and runs many
# queries — unoptimized SQLite dominates test runtime, worst on Windows CI.
# The `test` profile inherits these dev package overrides. Deliberately
# scoped to the libsql layers only; do not blanket-override "*".
# scoped to the libsql and tokenizer layers only; do not blanket-override "*".
[profile.dev.package.libsql-ffi]
opt-level = 2

Expand All @@ -172,3 +172,25 @@ opt-level = 2

[profile.dev.package.libsql-rusqlite]
opt-level = 2

# The first token count pays a one-time BPE model load (embedded-vocabulary
# base64 decode + BPE regex compile) that costs seconds in unoptimized
# builds; the dashboard token-count warm task and its tests hit it on every
# test-process start.
[profile.dev.package.tiktoken-rs]
opt-level = 2

[profile.dev.package.base64]
opt-level = 2

[profile.dev.package.fancy-regex]
opt-level = 2

[profile.dev.package.regex]
opt-level = 2

[profile.dev.package.regex-automata]
opt-level = 2

[profile.dev.package.regex-syntax]
opt-level = 2
92 changes: 70 additions & 22 deletions src/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,72 @@ use crate::branch_meta::BranchMeta;
///
/// Returns `None` for detached HEAD or if the repository cannot be opened.
pub fn current_branch(project_root: &Path) -> Option<String> {
if let Some(branch) = current_branch_gix(project_root) {
return Some(branch);
match current_branch_gix(project_root) {
GixHead::Branch(branch) => Some(branch),
// A readable repo answered with a detached HEAD; `git symbolic-ref`
// would fail the same way, so don't spawn it.
GixHead::Detached => None,
GixHead::Unavailable => {
if !crate::worktree::git_may_resolve_repo(project_root) {
return None;
}
current_branch_git(project_root)
}
}
current_branch_git(project_root)
}

/// Returns true if `branch` exists as a local `refs/heads/*` branch.
pub fn local_branch_exists(project_root: &Path, branch: &str) -> bool {
if branch.is_empty() {
return false;
}
let refname = format!("refs/heads/{branch}");
if let Ok(repo) = gix::open(project_root) {
let refname = format!("refs/heads/{branch}");
if repo.find_reference(&refname).is_ok() {
return true;
}
// gix reads loose and packed refs, the same sources `git show-ref`
// consults; trust its answer instead of paying a subprocess spawn
// to re-ask git.
return repo.find_reference(&refname).is_ok();
}
if !crate::worktree::git_may_resolve_repo(project_root) {
return false;
}
std::process::Command::new("git")
.args([
"show-ref",
"--verify",
"--quiet",
&format!("refs/heads/{branch}"),
])
.args(["show-ref", "--verify", "--quiet", &refname])
.current_dir(project_root)
.status()
.is_ok_and(|status| status.success())
}

fn current_branch_gix(project_root: &Path) -> Option<String> {
let repo = gix::open(project_root).ok()?;
let head = repo.head().ok()?;
let name = head.name().as_bstr();
let name_str = std::str::from_utf8(name).ok()?;
name_str
.strip_prefix("refs/heads/")
.map(std::string::ToString::to_string)
/// What gix could learn about HEAD without spawning `git`.
enum GixHead {
/// HEAD points at a local branch.
Branch(String),
/// A readable repo whose HEAD is detached (or on a non-branch ref).
Detached,
/// No repo could be opened at this path or its HEAD was unreadable;
/// the `git` subprocess fallback should decide.
Unavailable,
}

fn current_branch_gix(project_root: &Path) -> GixHead {
let Ok(repo) = gix::open(project_root) else {
return GixHead::Unavailable;
};
let Ok(head) = repo.head() else {
return GixHead::Unavailable;
};
// `Head::name()` is always the literal "HEAD"; the branch HEAD points
// to (if any) is the referent.
let Some(name) = head.referent_name() else {
return GixHead::Detached;
};
let Ok(name_str) = std::str::from_utf8(name.as_bstr()) else {
return GixHead::Unavailable;
};
match name_str.strip_prefix("refs/heads/") {
Some(branch) => GixHead::Branch(branch.to_string()),
None => GixHead::Detached,
}
}

fn current_branch_git(project_root: &Path) -> Option<String> {
Expand Down Expand Up @@ -80,6 +109,23 @@ fn git_rev_list_count(project_root: &Path, from_ref: &str, to_ref: &str) -> Opti
.ok()
}

/// In-process equivalent of `git rev-list --count hidden..tip`: commits
/// reachable from `tip` but not from `hidden`. Saves a `git` subprocess
/// spawn on every branch-add parent ranking.
fn gix_rev_distance(
repo: &gix::Repository,
tip: gix::ObjectId,
hidden: gix::ObjectId,
) -> Option<usize> {
let walk = repo.rev_walk([tip]).with_hidden([hidden]).all().ok()?;
let mut count = 0_usize;
for info in walk {
info.ok()?;
count += 1;
}
Some(count)
}

/// Auto-detects the default branch (main or master).
///
/// Strategy:
Expand Down Expand Up @@ -259,7 +305,9 @@ pub fn find_nearest_tracked_ancestor(
// branch. Rank them by commit distance so a direct parent wins even
// when multiple merge-bases land in the same timestamp second.
if base_id == tracked_commit.id {
if let Some(distance) = git_rev_list_count(project_root, &tracked_ref, &branch_ref) {
let distance = gix_rev_distance(&repo, branch_commit.id, tracked_commit.id)
.or_else(|| git_rev_list_count(project_root, &tracked_ref, &branch_ref));
if let Some(distance) = distance {
let replace = best_ancestor
.as_ref()
.is_none_or(|(_, best_distance, best_time)| {
Expand Down
10 changes: 10 additions & 0 deletions src/dashboard/token_count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,9 @@ mod tests {
assert!(!encoder_for_model("opus-large").exact);
}

// The two vocabulary tests are split so each test process pays only one
// BPE model load (the dominant cost, especially on Windows) and nextest
// can run them in parallel.
#[cfg(feature = "token-counting")]
#[test]
fn bpe_counts_diverge_from_chars4() {
Expand All @@ -513,8 +516,15 @@ mod tests {
// Code-heavy text tokenizes denser than chars/4 predicts; the exact
// value is vocabulary-dependent, so only sanity-bound it.
assert!(bpe <= text.len() as i64);
}

#[cfg(feature = "token-counting")]
#[test]
fn bpe_counts_use_cl100k_for_legacy_models() {
let text = "fn main() { println!(\"hello tokenizer world\"); }";
let cl = count_text_tokens(text, "gpt-4");
assert!(cl > 0);
assert!(cl <= text.len() as i64);
}

#[tokio::test]
Expand Down
6 changes: 6 additions & 0 deletions src/sessions/codex_app_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ impl Drop for ChildGuard {

#[cfg(windows)]
fn kill_child_process_tree(child: &mut Child) {
// `cmd /C` shims wait for their grandchildren, so a child that already
// exited has no live process tree left; skip the taskkill spawn that
// would fail anyway.
if matches!(child.try_wait(), Ok(Some(_))) {
return;
}
let _ = Command::new("taskkill")
.arg("/PID")
.arg(child.id().to_string())
Expand Down
13 changes: 13 additions & 0 deletions src/tracedecay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,19 @@ fn profile_store_id(project_id: &str) -> String {
}

fn git_remote_url(project_root: &Path) -> Option<String> {
// gix reads the same config `git config --get` would (repo-local +
// global) without a subprocess spawn.
if let Ok(repo) = gix::discover(project_root) {
let url = repo
.config_snapshot()
.string("remote.origin.url")?
.to_string();
let url = url.trim();
return (!url.is_empty()).then(|| url.to_string());
}
if !crate::worktree::git_may_resolve_repo(project_root) {
return None;
}
git_output(project_root, &["config", "--get", "remote.origin.url"])
}

Expand Down
33 changes: 33 additions & 0 deletions src/worktree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ pub struct WorktreeIndexMismatch {
/// checkout and each linked worktree report their own distinct directory,
/// which is exactly the distinction this module relies on.
pub fn git_worktree_root(dir: &Path) -> Option<PathBuf> {
// gix discovery walks up the same way `git rev-parse` does but without
// a subprocess spawn. A discovered bare repo (no workdir) matches
// `--show-toplevel` failing.
if let Ok(repo) = gix::discover(dir) {
return realpath(repo.workdir()?);
}
if !git_may_resolve_repo(dir) {
return None;
}
let trimmed = git_output(dir, &["rev-parse", "--show-toplevel"])?;
realpath(Path::new(&trimmed))
}
Expand All @@ -48,6 +57,18 @@ pub fn git_worktree_root(dir: &Path) -> Option<PathBuf> {
/// For a linked worktree this is the main checkout's `.git` directory, which is
/// the stable local identity all linked worktrees share.
pub fn git_common_dir(dir: &Path) -> Option<PathBuf> {
if let Ok(repo) = gix::discover(dir) {
let common_dir = repo.common_dir().to_path_buf();
let resolved = if common_dir.is_absolute() {
common_dir
} else {
dir.join(common_dir)
};
return Some(resolved.canonicalize().unwrap_or(resolved));
}
if !git_may_resolve_repo(dir) {
return None;
}
let raw = git_output(dir, &["rev-parse", "--git-common-dir"])?;
let common_dir = PathBuf::from(raw);
let resolved = if common_dir.is_absolute() {
Expand All @@ -58,6 +79,18 @@ pub fn git_common_dir(dir: &Path) -> Option<PathBuf> {
Some(resolved.canonicalize().unwrap_or(resolved))
}

/// Cheap pre-flight for the `git` subprocess fallbacks in this crate: `git`
/// can only resolve a repository for `dir` when a `.git` entry exists
/// somewhere in its ancestor chain or the caller overrides discovery via
/// `GIT_DIR`. Spawning `git` costs ~100-300ms on Windows, so callers skip
/// the spawn when it is guaranteed to fail anyway.
pub(crate) fn git_may_resolve_repo(dir: &Path) -> bool {
if std::env::var_os("GIT_DIR").is_some() {
return true;
}
dir.ancestors().any(|p| p.join(".git").exists())
}

/// Detect when `start_path` lives in one git working tree but the resolved
/// tracedecay index (`index_root`) belongs to a *different* working tree.
///
Expand Down
Loading
Loading