diff --git a/crates/ruff/build.rs b/crates/ruff/build.rs index 3615ee555d..bb5ac821bf 100644 --- a/crates/ruff/build.rs +++ b/crates/ruff/build.rs @@ -1,4 +1,8 @@ -use std::{fs, path::Path, process::Command}; +use std::{ + fs, + path::{Path, PathBuf}, + process::Command, +}; fn main() { // The workspace root directory is not available without walking up the tree @@ -21,27 +25,22 @@ fn commit_info(workspace_root: &Path) { return; } - let git_head_path = git_dir.join("HEAD"); - println!( - "cargo::rerun-if-changed={}", - git_head_path.as_path().display() - ); + if let Some(git_head_path) = git_head(&git_dir) { + println!("cargo:rerun-if-changed={}", git_head_path.display()); - let git_head_contents = fs::read_to_string(git_head_path); - if let Ok(git_head_contents) = git_head_contents { - // The contents are either a commit or a reference in the following formats - // - "" when the head is detached - // - "ref " when working on a branch - // If a commit, checking if the HEAD file has changed is sufficient - // If a ref, we need to add the head file for that ref to rebuild on commit - let mut git_ref_parts = git_head_contents.split_whitespace(); - git_ref_parts.next(); - if let Some(git_ref) = git_ref_parts.next() { - let git_ref_path = git_dir.join(git_ref); - println!( - "cargo::rerun-if-changed={}", - git_ref_path.as_path().display() - ); + let git_head_contents = fs::read_to_string(git_head_path); + if let Ok(git_head_contents) = git_head_contents { + // The contents are either a commit or a reference in the following formats + // - "" when the head is detached + // - "ref " when working on a branch + // If a commit, checking if the HEAD file has changed is sufficient + // If a ref, we need to add the head file for that ref to rebuild on commit + let mut git_ref_parts = git_head_contents.split_whitespace(); + git_ref_parts.next(); + if let Some(git_ref) = git_ref_parts.next() { + let git_ref_path = git_dir.join(git_ref); + println!("cargo:rerun-if-changed={}", git_ref_path.display()); + } } } @@ -78,3 +77,30 @@ fn commit_info(workspace_root: &Path) { ); } } + +fn git_head(git_dir: &Path) -> Option { + // The typical case is a standard git repository. + let git_head_path = git_dir.join("HEAD"); + if git_head_path.exists() { + return Some(git_head_path); + } + if !git_dir.is_file() { + return None; + } + // If `.git/HEAD` doesn't exist and `.git` is actually a file, + // then let's try to attempt to read it as a worktree. If it's + // a worktree, then its contents will look like this, e.g.: + // + // gitdir: /home/andrew/astral/uv/main/.git/worktrees/pr2 + // + // And the HEAD file we want to watch will be at: + // + // /home/andrew/astral/uv/main/.git/worktrees/pr2/HEAD + let contents = fs::read_to_string(git_dir).ok()?; + let (label, worktree_path) = contents.split_once(':')?; + if label != "gitdir" { + return None; + } + let worktree_path = worktree_path.trim(); + Some(PathBuf::from(worktree_path)) +}