| 316 | |
| 317 | impl Command for New { |
| 318 | fn run(&self) -> CliResult<()> { |
| 319 | // Get the view name |
| 320 | let name = self |
| 321 | .name |
| 322 | .as_ref() |
| 323 | .ok_or_else(|| CliError::InvalidArgument { |
| 324 | message: "View name is required".to_string(), |
| 325 | })?; |
| 326 | |
| 327 | // Validate the view name |
| 328 | validate_view_name(name).map_err(|msg| CliError::InvalidArgument { message: msg })?; |
| 329 | |
| 330 | // Find the repository |
| 331 | let repo_root = find_repository_root()?; |
| 332 | let mut repo = Repository::open(&repo_root).map_err(|e| match e { |
| 333 | atomic_repository::RepositoryError::NotFound { path } => CliError::RepositoryNotFound { |
| 334 | searched_path: path.into(), |
| 335 | }, |
| 336 | other => CliError::Repository(other), |
| 337 | })?; |
| 338 | |
| 339 | // Check if the view already exists |
| 340 | if repo.view_exists(name).map_err(CliError::Repository)? { |
| 341 | return Err(CliError::ViewAlreadyExists { |
| 342 | name: name.to_string(), |
| 343 | }); |
| 344 | } |
| 345 | |
| 346 | // If --draft or --parent is specified, use the two-tier create path |
| 347 | if self.draft || self.parent.is_some() { |
| 348 | return self.run_two_tier(name, &mut repo); |
| 349 | } |
| 350 | |
| 351 | // Determine how to create the new view: |
| 352 | // |
| 353 | // --from X → create Draft parented on X, insert X's changes |
| 354 | // default → create Draft parented on nearest Shared ancestor, |
| 355 | // with an EMPTY change log (no files until `insert`) |
| 356 | // |
| 357 | // The new view is a Draft workspace whose edges go to |
| 358 | // GRAPH (filtered by this view's change set). The parent link |
| 359 | // gives the overlay chain read-access to the shared graph for |
| 360 | // record-time diff computation. |
| 361 | // |
| 362 | // When --from is specified, the source's changes are inserted |
| 363 | // immediately so the new view starts with the source's files. |
| 364 | // Without --from, the change log starts empty — the user brings |
| 365 | // in changes explicitly via `insert from-view`. This is the |
| 366 | // normal workflow: |
| 367 | // |
| 368 | // atomic view create feature # empty workspace |
| 369 | // atomic insert from-view dev # inherit dev's files |
| 370 | // # ... make changes, record ... |
| 371 | // atomic insert from-view feature --to-view dev # promote |
| 372 | if let Some(ref source) = self.from { |
| 373 | // Explicit --from: fork from the specified view. |
| 374 | if !repo.view_exists(source).map_err(CliError::Repository)? { |
| 375 | return Err(CliError::ViewNotFound { |