From 1e2434425974055c8eb81f43e997cfd918e2770f Mon Sep 17 00:00:00 2001 From: "majin.nathan" Date: Tue, 9 Jun 2026 17:35:39 +0800 Subject: [PATCH 1/6] fix: reserve main branch name --- rust/lance/src/dataset.rs | 16 +++++---- rust/lance/src/dataset/refs.rs | 3 +- .../src/dataset/tests/dataset_versioning.rs | 36 +++++++++++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/rust/lance/src/dataset.rs b/rust/lance/src/dataset.rs index c9cc356aaa6..ee19127aea1 100644 --- a/rust/lance/src/dataset.rs +++ b/rust/lance/src/dataset.rs @@ -499,6 +499,8 @@ impl Dataset { version: impl Into, store_params: Option, ) -> Result { + refs::check_valid_branch(branch)?; + let (source_branch, version_number) = self.resolve_reference(version.into()).await?; let branch_location = self.branch_location().find_branch(Some(branch))?; let source_location = self @@ -560,16 +562,19 @@ impl Dataset { version_number: Option, branch: Option<&str>, ) -> Result { + let standardized_branch = branch.and_then(refs::standardize_branch); // Reject malformed names at the boundary (mirroring the branch CRUD // paths) so they fail as InvalidRef instead of tripping the wrong-chain // check below - if let Some(branch_name) = branch - && !Branches::is_main_branch(branch) + if let Some(branch_name) = standardized_branch.as_deref() + && !Branches::is_main_branch(Some(branch_name)) { refs::check_valid_branch(branch_name)?; } - let new_location = self.branch_location().find_branch(branch)?; + let new_location = self + .branch_location() + .find_branch(standardized_branch.as_deref())?; let manifest_location = if let Some(version_number) = version_number { self.commit_handler @@ -585,7 +590,7 @@ impl Dataset { .await? }; - if self.already_checked_out(&manifest_location, branch) { + if self.already_checked_out(&manifest_location, standardized_branch.as_deref()) { return Ok(self.clone()); } @@ -601,8 +606,7 @@ impl Dataset { // means the commit handler resolved against a different chain (for // example an external manifest store that ignores branch-qualified // paths); error loudly rather than hand back another branch's data. - let requested_branch = branch.and_then(refs::standardize_branch); - if manifest.branch.as_deref() != requested_branch.as_deref() { + if manifest.branch.as_deref() != standardized_branch.as_deref() { return Err(Error::internal(format!( "checkout of branch '{}' at version {} resolved a manifest belonging to branch '{}'", refs::normalize_branch(branch), diff --git a/rust/lance/src/dataset/refs.rs b/rust/lance/src/dataset/refs.rs index 98b4f0cbc0a..e73e40498e5 100644 --- a/rust/lance/src/dataset/refs.rs +++ b/rust/lance/src/dataset/refs.rs @@ -1020,7 +1020,8 @@ pub fn check_valid_branch(branch_name: &str) -> Result<()> { if branch_name.eq("main") { return Err(Error::InvalidRef { - message: "Branch name cannot be 'main'".to_string(), + message: "\"main\" is reserved for the default branch; use a different branch name" + .to_string(), }); } Ok(()) diff --git a/rust/lance/src/dataset/tests/dataset_versioning.rs b/rust/lance/src/dataset/tests/dataset_versioning.rs index a0bc7816a32..c69146612c5 100644 --- a/rust/lance/src/dataset/tests/dataset_versioning.rs +++ b/rust/lance/src/dataset/tests/dataset_versioning.rs @@ -624,6 +624,42 @@ async fn test_create_branch_and_shallow_clone_from_other_branch() { ); } +#[tokio::test] +async fn test_main_branch_management() { + let tempdir = TempDir::default(); + let test_uri = tempdir.path_str(); + let data = gen_batch() + .col("id", array::step::()) + .into_reader_rows(RowCount::from(10), BatchCount::from(1)); + let mut dataset = Dataset::write(data, &test_uri, None).await.unwrap(); + + let checked_out = dataset.checkout_branch("main").await.unwrap(); + assert_eq!(checked_out.version().version, dataset.version().version); + assert_eq!(checked_out.manifest.branch, None); + + let checked_out = dataset.checkout_version(("main", None)).await.unwrap(); + assert_eq!(checked_out.version().version, dataset.version().version); + assert_eq!(checked_out.manifest.branch, None); + + let err = match dataset.create_branch("main", ("main", None), None).await { + Ok(_) => panic!("creating a branch named main should fail"), + Err(err) => err, + }; + assert!(matches!(err, Error::InvalidRef { .. })); + assert!(err.to_string().contains("\"main\" is reserved"), "{err}"); + + assert!(!tempdir.std_path().join("tree").join("main").exists()); + assert!(dataset.list_branches().await.unwrap().is_empty()); + + let err = dataset.branches().get("main").await.unwrap_err(); + assert!(matches!(err, Error::InvalidRef { .. })); + assert!(err.to_string().contains("\"main\" is reserved"), "{err}"); + + let err = dataset.delete_branch("main").await.unwrap_err(); + assert!(matches!(err, Error::InvalidRef { .. })); + assert!(err.to_string().contains("\"main\" is reserved"), "{err}"); +} + #[tokio::test] async fn test_branch() { let tempdir = TempDir::default(); From ab6be65c551279e7e69acd8fad2364cb5d889a13 Mon Sep 17 00:00:00 2001 From: "majin.nathan" Date: Tue, 9 Jun 2026 20:23:58 +0800 Subject: [PATCH 2/6] test: clean up main branch management test --- .../src/dataset/tests/dataset_versioning.rs | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/rust/lance/src/dataset/tests/dataset_versioning.rs b/rust/lance/src/dataset/tests/dataset_versioning.rs index c69146612c5..5aeb841e128 100644 --- a/rust/lance/src/dataset/tests/dataset_versioning.rs +++ b/rust/lance/src/dataset/tests/dataset_versioning.rs @@ -633,31 +633,49 @@ async fn test_main_branch_management() { .into_reader_rows(RowCount::from(10), BatchCount::from(1)); let mut dataset = Dataset::write(data, &test_uri, None).await.unwrap(); - let checked_out = dataset.checkout_branch("main").await.unwrap(); - assert_eq!(checked_out.version().version, dataset.version().version); - assert_eq!(checked_out.manifest.branch, None); + let main_branch_checkout = dataset.checkout_branch("main").await.unwrap(); + assert_eq!( + main_branch_checkout.version().version, + dataset.version().version + ); + assert_eq!(main_branch_checkout.manifest.branch, None); - let checked_out = dataset.checkout_version(("main", None)).await.unwrap(); - assert_eq!(checked_out.version().version, dataset.version().version); - assert_eq!(checked_out.manifest.branch, None); + let main_ref_checkout = dataset.checkout_version(("main", None)).await.unwrap(); + assert_eq!( + main_ref_checkout.version().version, + dataset.version().version + ); + assert_eq!(main_ref_checkout.manifest.branch, None); - let err = match dataset.create_branch("main", ("main", None), None).await { - Ok(_) => panic!("creating a branch named main should fail"), - Err(err) => err, + let Err(create_branch_err) = dataset.create_branch("main", ("main", None), None).await else { + panic!("creating a branch named main should fail"); }; - assert!(matches!(err, Error::InvalidRef { .. })); - assert!(err.to_string().contains("\"main\" is reserved"), "{err}"); + assert!(matches!(create_branch_err, Error::InvalidRef { .. })); + assert!( + create_branch_err + .to_string() + .contains("\"main\" is reserved"), + "{create_branch_err}" + ); assert!(!tempdir.std_path().join("tree").join("main").exists()); assert!(dataset.list_branches().await.unwrap().is_empty()); - let err = dataset.branches().get("main").await.unwrap_err(); - assert!(matches!(err, Error::InvalidRef { .. })); - assert!(err.to_string().contains("\"main\" is reserved"), "{err}"); + let get_branch_err = dataset.branches().get("main").await.unwrap_err(); + assert!(matches!(get_branch_err, Error::InvalidRef { .. })); + assert!( + get_branch_err.to_string().contains("\"main\" is reserved"), + "{get_branch_err}" + ); - let err = dataset.delete_branch("main").await.unwrap_err(); - assert!(matches!(err, Error::InvalidRef { .. })); - assert!(err.to_string().contains("\"main\" is reserved"), "{err}"); + let delete_branch_err = dataset.delete_branch("main").await.unwrap_err(); + assert!(matches!(delete_branch_err, Error::InvalidRef { .. })); + assert!( + delete_branch_err + .to_string() + .contains("\"main\" is reserved"), + "{delete_branch_err}" + ); } #[tokio::test] From 704b23451db4d2ff262d9714b9cddbb025843c76 Mon Sep 17 00:00:00 2001 From: "majin.nathan" Date: Tue, 9 Jun 2026 20:31:00 +0800 Subject: [PATCH 3/6] test: simplify main branch test names --- .../src/dataset/tests/dataset_versioning.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/rust/lance/src/dataset/tests/dataset_versioning.rs b/rust/lance/src/dataset/tests/dataset_versioning.rs index 5aeb841e128..12a6117e4ae 100644 --- a/rust/lance/src/dataset/tests/dataset_versioning.rs +++ b/rust/lance/src/dataset/tests/dataset_versioning.rs @@ -633,19 +633,13 @@ async fn test_main_branch_management() { .into_reader_rows(RowCount::from(10), BatchCount::from(1)); let mut dataset = Dataset::write(data, &test_uri, None).await.unwrap(); - let main_branch_checkout = dataset.checkout_branch("main").await.unwrap(); - assert_eq!( - main_branch_checkout.version().version, - dataset.version().version - ); - assert_eq!(main_branch_checkout.manifest.branch, None); + let main_branch = dataset.checkout_branch("main").await.unwrap(); + assert_eq!(main_branch.version().version, dataset.version().version); + assert_eq!(main_branch.manifest.branch, None); - let main_ref_checkout = dataset.checkout_version(("main", None)).await.unwrap(); - assert_eq!( - main_ref_checkout.version().version, - dataset.version().version - ); - assert_eq!(main_ref_checkout.manifest.branch, None); + let main_ref = dataset.checkout_version(("main", None)).await.unwrap(); + assert_eq!(main_ref.version().version, dataset.version().version); + assert_eq!(main_ref.manifest.branch, None); let Err(create_branch_err) = dataset.create_branch("main", ("main", None), None).await else { panic!("creating a branch named main should fail"); From 85c01f01e20fd827baa0efc5979970ea1889c5ef Mon Sep 17 00:00:00 2001 From: "majin.nathan" Date: Thu, 11 Jun 2026 13:37:08 +0800 Subject: [PATCH 4/6] docs: clarify main branch reference semantics --- docs/src/format/table/branch_tag.md | 8 +++++--- docs/src/guide/tags_and_branches.md | 6 +++++- docs/src/quickstart/versioning.md | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/src/format/table/branch_tag.md b/docs/src/format/table/branch_tag.md index d4784a288e9..bbca8fa73b6 100644 --- a/docs/src/format/table/branch_tag.md +++ b/docs/src/format/table/branch_tag.md @@ -17,7 +17,9 @@ Branch names must follow these validation rules: 4. Cannot contain `..` or `\` 5. Segments must contain only alphanumeric characters, `.`, `-`, `_` 6. Cannot end with `.lock` -7. Cannot be named `main` (reserved for main branch) +7. Cannot be named `main` (reserved for the default branch) + +`main` is a virtual name for the default branch. It may appear in API reference contexts as an alias for the default branch, but no branch metadata file named `main.json` is created. ### Branch Metadata Path @@ -38,7 +40,7 @@ Each branch metadata file is a JSON file with the following fields: | JSON Key | Type | Optional | Description | |------------------|--------|----------|--------------------------------------------------------------------------------| -| `parentBranch` | string | Yes | Name of the branch this was created from. `null` indicates branched from main. | +| `parentBranch` | string | Yes | Name of the branch this was created from. `null` indicates branched from the default branch. | | `parentVersion` | number | | Version number of the parent branch at the time this branch was created. | | `createAt` | number | | Unix timestamp (seconds since epoch) when the branch was created. | | `manifestSize` | number | | Size of the initial manifest file in bytes. | @@ -117,7 +119,7 @@ Each tag file is a JSON file with the following fields: | JSON Key | Type | Optional | Description | |-----------------|--------|----------|--------------------------------------------------------------------------| -| `branch` | string | Yes | Branch name being tagged. `null` or absent indicates main branch. | +| `branch` | string | Yes | Branch name being tagged. `null` or absent indicates the default branch. | | `version` | number | | Version number being tagged within that branch. | | `createdAt` | string | Yes | RFC 3339 timestamp for when the tag was first created. | | `updatedAt` | string | Yes | RFC 3339 timestamp for the latest tag reference update. | diff --git a/docs/src/guide/tags_and_branches.md b/docs/src/guide/tags_and_branches.md index 8af2302bfb0..7d0e1f3802e 100644 --- a/docs/src/guide/tags_and_branches.md +++ b/docs/src/guide/tags_and_branches.md @@ -20,6 +20,8 @@ The `reference` parameter (used in `create`, `update`, and `checkout_version`) a - `("experiment", 3)` means version 3 on the experiment branch - `("branch-name", None)` means the latest version on that branch +In reference contexts, `"main"` is an alias for the default branch and is equivalent to `None`. + !!! note Creating or deleting tags does not generate new dataset versions. @@ -77,7 +79,7 @@ The `reference` parameter works the same as for Tags (see above). Each branch maintains its own linear version history, so version numbers may overlap across branches. Use `(branch_name, version_number)` tuples as global identifiers for operations like `checkout_version` and `tags.create`. - "main" is a reserved branch name. Lance uses "main" to identify the default branch. + `"main"` is reserved for the default branch. Use `"main"` or `None` when referring to the default branch in reference tuples or checkout APIs, but choose a different name when creating, deleting, or updating branches. ### Create and checkout branches ```python @@ -99,6 +101,8 @@ ds.tags.create("experiment-rc", ("experiment", None)) experiment_rc = ds.checkout_version("experiment-rc") # Checkout the latest version of the experimental branch by tuple experiment_latest = ds.checkout_version(("experiment", None)) +# Checkout the latest version of the default branch explicitly +main_latest = ds.checkout_version(("main", None)) # Create a new branch from a tag new_experiment = ds.create_branch("new-experiment", "experiment-rc") diff --git a/docs/src/quickstart/versioning.md b/docs/src/quickstart/versioning.md index 8cdf1cb35ea..acebe139d57 100644 --- a/docs/src/quickstart/versioning.md +++ b/docs/src/quickstart/versioning.md @@ -95,6 +95,8 @@ For advanced tag operations (e.g., tagging versions on specific branches), see [ Branches manage parallel lines of dataset evolution. You can create branches from existing versions or tags, read and write to them independently, and checkout different branches. +`main` refers to the default branch in checkout and reference APIs, but it is reserved and cannot be used as the name of a new branch. + ```python # Create branch from current latest version experiment_branch = ds.create_branch("experiment") From 26350af1795b00bd07803ce269f7b6a1a5767b4e Mon Sep 17 00:00:00 2001 From: "majin.nathan" Date: Thu, 11 Jun 2026 14:14:50 +0800 Subject: [PATCH 5/6] docs: clarify main branch API semantics --- docs/src/guide/tags_and_branches.md | 4 +-- java/src/main/java/org/lance/Dataset.java | 36 +++++++++++++++-------- java/src/main/java/org/lance/Ref.java | 12 ++++++++ python/python/lance/dataset.py | 18 ++++++++---- rust/lance/src/dataset.rs | 11 +++++-- 5 files changed, 58 insertions(+), 23 deletions(-) diff --git a/docs/src/guide/tags_and_branches.md b/docs/src/guide/tags_and_branches.md index 7d0e1f3802e..5a7f8639607 100644 --- a/docs/src/guide/tags_and_branches.md +++ b/docs/src/guide/tags_and_branches.md @@ -15,8 +15,8 @@ The `reference` parameter (used in `create`, `update`, and `checkout_version`) a - An **integer**: version number in the **current branch** (e.g., `1`) - A **string**: tag name (e.g., `"stable"`) - A **tuple** `(branch_name, version)`: a specific version in a named branch - - `(None, 2)` means version 2 on the main branch - - `("main", 2)` means version 2 on the main branch (explicit) + - `(None, 2)` means version 2 on the default branch + - `("main", 2)` means version 2 on the default branch (explicit) - `("experiment", 3)` means version 3 on the experiment branch - `("branch-name", None)` means the latest version on that branch diff --git a/java/src/main/java/org/lance/Dataset.java b/java/src/main/java/org/lance/Dataset.java index 23341283861..4881ce8c7a9 100644 --- a/java/src/main/java/org/lance/Dataset.java +++ b/java/src/main/java/org/lance/Dataset.java @@ -1690,10 +1690,12 @@ public Branches branches() { /** * Create a branch at a specified version. The returned Dataset points to the created branch's - * initial version. + * initial version. The branch name {@code "main"} is reserved for the default branch and cannot + * be used as a new branch name. * * @param branch the branch name to create - * @param ref the reference to create branch from + * @param ref the reference to create branch from. In reference contexts, {@code "main"} is an + * alias for the default branch. * @return a new Dataset of the branch */ public Dataset createBranch(String branch, Ref ref) { @@ -1703,10 +1705,12 @@ public Dataset createBranch(String branch, Ref ref) { /** * Create a branch at a specified version. The returned Dataset points to the created branch's - * initial version. + * initial version. The branch name {@code "main"} is reserved for the default branch and cannot + * be used as a new branch name. * * @param branch the branch name to create - * @param ref the reference to create branch from + * @param ref the reference to create branch from. In reference contexts, {@code "main"} is an + * alias for the default branch. * @param storageOptions the storage options to create branch with * @return a new Dataset of the branch */ @@ -1727,8 +1731,9 @@ private Dataset innerCreateBranch( } /** - * Checkout using a unified {@link Ref} which can be a tag, the latest version on main/branch or a - * specified (branch_name, version_number). + * Checkout using a unified {@link Ref} which can be a tag, the latest version on the default + * branch or a named branch, or a specified (branch_name, version_number). In reference contexts, + * {@code "main"} is an alias for the default branch. * * @param ref the checkout reference * @return a new Dataset instance checked out to the specified reference @@ -1765,7 +1770,7 @@ public Map getTableMetadata() { public class Tags { /** - * Create a new tag on main branch. This is left for compatibility. We should use {@link + * Create a new tag on the default branch. This is left for compatibility. We should use {@link * #create(String, Ref)} instead. * * @param tag the tag name @@ -1780,7 +1785,8 @@ public void create(String tag, long versionNumber) { * Create a new tag on a specified branch. * * @param tag the tag name - * @param ref the referenced version to tag + * @param ref the referenced version to tag. In reference contexts, {@code "main"} is an alias + * for the default branch. */ public void create(String tag, Ref ref) { Preconditions.checkArgument(tag != null, "Tag name cannot be null"); @@ -1797,6 +1803,8 @@ public void create(String tag, Ref ref) { * * @param tag the name of the tag to create * @param versionNumber the version number (or commit reference) to associate with the tag + * @param targetBranch the branch to tag. In reference contexts, {@code "main"} is an alias for + * the default branch. */ @Deprecated public void create(String tag, long versionNumber, String targetBranch) { @@ -1816,11 +1824,11 @@ public void delete(String tag) { } /** - * Update a tag to a new version_number on main. This is left for compatibility. We should use - * {@link #update(String, Ref)} instead. + * Update a tag to a new version_number on the default branch. This is left for compatibility. + * We should use {@link #update(String, Ref)} instead. * * @param tag the tag name - * @param versionNumber the versionNumber on main. + * @param versionNumber the versionNumber on the default branch. */ public void update(String tag, long versionNumber) { Preconditions.checkArgument(versionNumber > 0, "version_number must be greater than 0"); @@ -1831,7 +1839,8 @@ public void update(String tag, long versionNumber) { * Update a tag to a new reference. * * @param tag the tag name - * @param ref the referenced version to tag + * @param ref the referenced version to tag. In reference contexts, {@code "main"} is an alias + * for the default branch. */ public void update(String tag, Ref ref) { Preconditions.checkArgument(tag != null, "tag cannot be null"); @@ -1883,7 +1892,8 @@ public class Branches { /** * Delete a branch and its metadata. * - * @param branchName the branch to delete + * @param branchName the branch to delete. {@code "main"} is reserved for the default branch and + * cannot be deleted as a named branch. */ public void delete(String branchName) { try (LockManager.WriteLock writeLock = lockManager.acquireWriteLock()) { diff --git a/java/src/main/java/org/lance/Ref.java b/java/src/main/java/org/lance/Ref.java index 111a1edd6d3..69e0e3fb8f3 100644 --- a/java/src/main/java/org/lance/Ref.java +++ b/java/src/main/java/org/lance/Ref.java @@ -41,21 +41,33 @@ public Optional getTagName() { return tagName; } + /** Creates a reference to a specific version on the default branch. */ public static Ref ofMain(long versionNumber) { Preconditions.checkArgument(versionNumber > 0, "versionNumber must be greater than 0"); return new Ref(Optional.of(versionNumber), Optional.empty(), Optional.empty()); } + /** Creates a reference to the latest version on the default branch. */ public static Ref ofMain() { return new Ref(Optional.empty(), Optional.empty(), Optional.empty()); } + /** + * Creates a reference to the latest version on a branch. + * + *

In reference contexts, {@code "main"} is an alias for the default branch. + */ public static Ref ofBranch(String branchName) { Preconditions.checkArgument( branchName != null && !branchName.isEmpty(), "branchName must not be empty"); return new Ref(Optional.empty(), Optional.of(branchName), Optional.empty()); } + /** + * Creates a reference to a specific version on a branch. + * + *

In reference contexts, {@code "main"} is an alias for the default branch. + */ public static Ref ofBranch(String branchName, long versionNumber) { Preconditions.checkArgument( branchName != null && !branchName.isEmpty(), "branchName must not be empty"); diff --git a/python/python/lance/dataset.py b/python/python/lance/dataset.py index e96d9305ce5..55a1ef0b09c 100644 --- a/python/python/lance/dataset.py +++ b/python/python/lance/dataset.py @@ -926,12 +926,14 @@ def create_branch( Parameters ---------- branch: str - Name of the branch to create. + Name of the branch to create. ``"main"`` is reserved for the + default branch and cannot be used as a new branch name. reference: Optional[int | str | Tuple[Optional[str], Optional[int]] An integer specifies a version number in the current branch; a string specifies a tag name; a Tuple[Optional[str], Optional[int]] specifies a version number in a specified branch. (None, None) means the latest - version_number on the main branch. + version_number on the default branch. ``("main", version)`` is an + explicit alias for the default branch in this reference context. storage_options: Optional[Dict[str, str]] Storage options for the underlying object store. If not provided, the storage options from the current dataset will be used. @@ -2863,7 +2865,8 @@ def checkout_version( An integer specifies a version number in the current branch; a string specifies a tag name; a Tuple[Optional[str], Optional[int]] specifies a version number in a specified branch. (None, None) means the latest - version_number on the main branch. + version_number on the default branch. ``("main", version)`` is an + explicit alias for the default branch in this reference context. Returns ------- @@ -4616,7 +4619,8 @@ def shallow_clone( An integer specifies a version number in the current branch; a string specifies a tag name; a Tuple[Optional[str], Optional[int]] specifies a version number in a specified branch. (None, None) means the latest - version_number on the main branch. + version_number on the default branch. ``("main", version)`` is an + explicit alias for the default branch in this reference context. storage_options : dict, optional Object store configuration for the new dataset (e.g., credentials, endpoints). If not specified, the storage options of the source dataset @@ -6930,7 +6934,8 @@ def create( An integer specifies a version number in the current branch; a string specifies a tag name; a Tuple[Optional[str], Optional[int]] specifies a version number in a specified branch. (None, None) means the latest - version_number on the main branch. + version_number on the default branch. ``("main", version)`` is an + explicit alias for the default branch in this reference context. """ self._ds.create_tag(tag, reference) @@ -6962,7 +6967,8 @@ def update( An integer specifies a version number in the current branch; a string specifies a tag name; a Tuple[Optional[str], Optional[int]] specifies a version number in a specified branch. (None, None) means the latest - version_number on the main branch. + version_number on the default branch. ``("main", version)`` is an + explicit alias for the default branch in this reference context. """ self._ds.update_tag(tag, reference) diff --git a/rust/lance/src/dataset.rs b/rust/lance/src/dataset.rs index ee19127aea1..fcdbf1fdff9 100644 --- a/rust/lance/src/dataset.rs +++ b/rust/lance/src/dataset.rs @@ -430,7 +430,10 @@ impl Dataset { DatasetBuilder::from_uri(uri).load().await } - /// Check out a dataset version with a ref + /// Check out a dataset version with a ref. + /// + /// In reference contexts, `"main"` is an alias for the default branch and + /// is equivalent to `None`. pub async fn checkout_version(&self, version: impl Into) -> Result { let reference: refs::Ref = version.into(); match reference { @@ -473,7 +476,9 @@ impl Dataset { Ok(()) } - /// Check out the latest version of the branch + /// Check out the latest version of the branch. + /// + /// Use `"main"` to check out the latest version of the default branch. pub async fn checkout_branch(&self, branch: &str) -> Result { self.checkout_by_ref(None, Some(branch)).await } @@ -493,6 +498,8 @@ impl Dataset { /// which can be cleaned up later. Such a zombie dataset may cause a branch creation /// failure if we use the same name to `create_branch`. In that case, you need to call /// `force_delete_branch` to interactively clean up the zombie dataset. + /// + /// `"main"` is reserved for the default branch and cannot be used as a new branch name. pub async fn create_branch( &mut self, branch: &str, From 97d597e916a30098b90464c28f5aac1fd899b269 Mon Sep 17 00:00:00 2001 From: "majin.nathan" Date: Fri, 12 Jun 2026 01:24:26 +0800 Subject: [PATCH 6/6] docs: clarify branch name case sensitivity --- docs/src/format/table/branch_tag.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/format/table/branch_tag.md b/docs/src/format/table/branch_tag.md index bbca8fa73b6..b34149bb3f9 100644 --- a/docs/src/format/table/branch_tag.md +++ b/docs/src/format/table/branch_tag.md @@ -19,7 +19,7 @@ Branch names must follow these validation rules: 6. Cannot end with `.lock` 7. Cannot be named `main` (reserved for the default branch) -`main` is a virtual name for the default branch. It may appear in API reference contexts as an alias for the default branch, but no branch metadata file named `main.json` is created. +Branch names are case-sensitive, matching Git/GitHub ref semantics. The exact name `main` is a virtual name for the default branch. It may appear in API reference contexts as an alias for the default branch, but no branch metadata file named `main.json` is created. ### Branch Metadata Path