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