Promote the current view's changes into its parent view. This is the behavior of a bare `atomic insert` (no change hash and no subcommand). The source is always the current view; the target defaults to the current view's parent but can be overridden with `--to`/`--view`. The working copy is intentionally NOT rematerialized: the target view is not checked out, so the current view's on-disk state
(repo: &Repository, args: &Insert)
| 270 | /// The working copy is intentionally NOT rematerialized: the target view is |
| 271 | /// not checked out, so the current view's on-disk state is unchanged. |
| 272 | fn run_promote_to_parent(repo: &Repository, args: &Insert) -> CliResult<()> { |
| 273 | let source = repo.current_view().to_string(); |
| 274 | let source_info = repo |
| 275 | .get_view_info(&source) |
| 276 | .map_err(|e| CliError::Internal(anyhow::anyhow!("{}", e)))?; |
| 277 | |
| 278 | // Resolve the target: explicit --to/--view, else the current view's parent. |
| 279 | let target = match args.view.as_deref() { |
| 280 | Some(v) => v.to_string(), |
| 281 | None => source_info |
| 282 | .parent_name |
| 283 | .clone() |
| 284 | .ok_or_else(|| CliError::InvalidArgument { |
| 285 | message: format!( |
| 286 | "'{source}' is a root view — there is no parent to insert into.\n \ |
| 287 | Use 'atomic insert from-view <source>' or pass --to <view> \ |
| 288 | to choose a target." |
| 289 | ), |
| 290 | })?, |
| 291 | }; |
| 292 | |
| 293 | if target == source { |
| 294 | return Err(CliError::InvalidArgument { |
| 295 | message: format!( |
| 296 | "Source and target are the same view ('{source}'). \ |
| 297 | Pass --to <view> to insert somewhere else." |
| 298 | ), |
| 299 | }); |
| 300 | } |
| 301 | |
| 302 | // Figure out what would move so we can show a count and short-circuit. |
| 303 | let missing = repo |
| 304 | .get_missing_changes_between(&source, Some(&target)) |
| 305 | .map_err(|e| CliError::Conflict { |
| 306 | description: e.to_string(), |
| 307 | })?; |
| 308 | |
| 309 | if missing.is_empty() { |
| 310 | output::print_success(&format!( |
| 311 | "Already even with '{target}' — nothing to insert." |
| 312 | )); |
| 313 | return Ok(()); |
| 314 | } |
| 315 | |
| 316 | output::print_info(&format!( |
| 317 | "Inserting {} change(s): {source} → {target}", |
| 318 | missing.len() |
| 319 | )); |
| 320 | |
| 321 | // Dry run: list the changes and stop before mutating. |
| 322 | if args.dry_run { |
| 323 | println!(); |
| 324 | for (i, hash) in missing.iter().enumerate() { |
| 325 | if let Ok(change) = repo.load_change(hash) { |
| 326 | let message = &change.hashed.header.message; |
| 327 | let short_msg = if message.len() > 50 { |
| 328 | format!("{}...", &message[..47]) |
| 329 | } else { |
no test coverage detected