just playing with tangled
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at gvimdiff 201 lines 7.6 kB view raw
1// Copyright 2020 The Jujutsu Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15use std::fs; 16 17use itertools::Itertools as _; 18use jj_lib::commit::CommitIteratorExt as _; 19use jj_lib::file_util; 20use jj_lib::file_util::IoResultExt as _; 21use jj_lib::ref_name::WorkspaceNameBuf; 22use jj_lib::repo::Repo as _; 23use jj_lib::rewrite::merge_commit_trees; 24use jj_lib::workspace::Workspace; 25use tracing::instrument; 26 27use crate::cli_util::CommandHelper; 28use crate::cli_util::RevisionArg; 29use crate::command_error::internal_error_with_message; 30use crate::command_error::user_error; 31use crate::command_error::CommandError; 32use crate::ui::Ui; 33 34/// How to handle sparse patterns when creating a new workspace. 35#[derive(clap::ValueEnum, Clone, Debug, Eq, PartialEq)] 36enum SparseInheritance { 37 /// Copy all sparse patterns from the current workspace. 38 Copy, 39 /// Include all files in the new workspace. 40 Full, 41 /// Clear all files from the workspace (it will be empty). 42 Empty, 43} 44 45/// Add a workspace 46/// 47/// By default, the new workspace inherits the sparse patterns of the current 48/// workspace. You can override this with the `--sparse-patterns` option. 49#[derive(clap::Args, Clone, Debug)] 50pub struct WorkspaceAddArgs { 51 /// Where to create the new workspace 52 destination: String, 53 /// A name for the workspace 54 /// 55 /// To override the default, which is the basename of the destination 56 /// directory. 57 #[arg(long)] 58 name: Option<WorkspaceNameBuf>, 59 /// A list of parent revisions for the working-copy commit of the newly 60 /// created workspace. You may specify nothing, or any number of parents. 61 /// 62 /// If no revisions are specified, the new workspace will be created, and 63 /// its working-copy commit will exist on top of the parent(s) of the 64 /// working-copy commit in the current workspace, i.e. they will share the 65 /// same parent(s). 66 /// 67 /// If any revisions are specified, the new workspace will be created, and 68 /// the new working-copy commit will be created with all these revisions as 69 /// parents, i.e. the working-copy commit will exist as if you had run `jj 70 /// new r1 r2 r3 ...`. 71 #[arg(long, short, value_name = "REVSETS")] 72 revision: Vec<RevisionArg>, 73 /// How to handle sparse patterns when creating a new workspace. 74 #[arg(long, value_enum, default_value_t = SparseInheritance::Copy)] 75 sparse_patterns: SparseInheritance, 76} 77 78#[instrument(skip_all)] 79pub fn cmd_workspace_add( 80 ui: &mut Ui, 81 command: &CommandHelper, 82 args: &WorkspaceAddArgs, 83) -> Result<(), CommandError> { 84 let old_workspace_command = command.workspace_helper(ui)?; 85 let destination_path = command.cwd().join(&args.destination); 86 if destination_path.exists() { 87 return Err(user_error("Workspace already exists")); 88 } else { 89 fs::create_dir(&destination_path).context(&destination_path)?; 90 } 91 let workspace_name = if let Some(name) = &args.name { 92 name.to_owned() 93 } else { 94 let file_name = destination_path.file_name().unwrap(); 95 file_name 96 .to_str() 97 .ok_or_else(|| user_error("Destination path is not valid UTF-8"))? 98 .into() 99 }; 100 let repo = old_workspace_command.repo(); 101 if repo.view().get_wc_commit_id(&workspace_name).is_some() { 102 return Err(user_error(format!( 103 "Workspace named '{name}' already exists", 104 name = workspace_name.as_symbol() 105 ))); 106 } 107 108 let working_copy_factory = command.get_working_copy_factory()?; 109 let repo_path = old_workspace_command.repo_path(); 110 // If we add per-workspace configuration, we'll need to reload settings for 111 // the new workspace. 112 let (new_workspace, repo) = Workspace::init_workspace_with_existing_repo( 113 &destination_path, 114 repo_path, 115 repo, 116 working_copy_factory, 117 workspace_name.clone(), 118 )?; 119 writeln!( 120 ui.status(), 121 "Created workspace in \"{}\"", 122 file_util::relative_path(command.cwd(), &destination_path).display() 123 )?; 124 // Show a warning if the user passed a path without a separator, since they 125 // may have intended the argument to only be the name for the workspace. 126 if !args.destination.contains(std::path::is_separator) { 127 writeln!( 128 ui.warning_default(), 129 r#"Workspace created inside current directory. If this was unintentional, delete the "{}" directory and run `jj workspace forget {name}` to remove it."#, 130 args.destination, 131 name = workspace_name.as_symbol() 132 )?; 133 } 134 135 let mut new_workspace_command = command.for_workable_repo(ui, new_workspace, repo)?; 136 137 let sparsity = match args.sparse_patterns { 138 SparseInheritance::Full => None, 139 SparseInheritance::Empty => Some(vec![]), 140 SparseInheritance::Copy => { 141 let sparse_patterns = old_workspace_command 142 .working_copy() 143 .sparse_patterns()? 144 .to_vec(); 145 Some(sparse_patterns) 146 } 147 }; 148 149 if let Some(sparse_patterns) = sparsity { 150 let checkout_options = new_workspace_command.checkout_options(); 151 let (mut locked_ws, _wc_commit) = new_workspace_command.start_working_copy_mutation()?; 152 locked_ws 153 .locked_wc() 154 .set_sparse_patterns(sparse_patterns, &checkout_options) 155 .map_err(|err| internal_error_with_message("Failed to set sparse patterns", err))?; 156 let operation_id = locked_ws.locked_wc().old_operation_id().clone(); 157 locked_ws.finish(operation_id)?; 158 } 159 160 let mut tx = new_workspace_command.start_transaction(); 161 162 // If no parent revisions are specified, create a working-copy commit based 163 // on the parent of the current working-copy commit. 164 let parents = if args.revision.is_empty() { 165 // Check out parents of the current workspace's working-copy commit, or the 166 // root if there is no working-copy commit in the current workspace. 167 if let Some(old_wc_commit_id) = tx 168 .base_repo() 169 .view() 170 .get_wc_commit_id(old_workspace_command.workspace_name()) 171 { 172 tx.repo() 173 .store() 174 .get_commit(old_wc_commit_id)? 175 .parents() 176 .try_collect()? 177 } else { 178 vec![tx.repo().store().root_commit()] 179 } 180 } else { 181 old_workspace_command 182 .resolve_some_revsets_default_single(ui, &args.revision)? 183 .iter() 184 .map(|id| tx.repo().store().get_commit(id)) 185 .try_collect()? 186 }; 187 188 let tree = merge_commit_trees(tx.repo(), &parents)?; 189 let parent_ids = parents.iter().ids().cloned().collect_vec(); 190 let new_wc_commit = tx.repo_mut().new_commit(parent_ids, tree.id()).write()?; 191 192 tx.edit(&new_wc_commit)?; 193 tx.finish( 194 ui, 195 format!( 196 "create initial working-copy commit in workspace {name}", 197 name = workspace_name.as_symbol() 198 ), 199 )?; 200 Ok(()) 201}