just playing with tangled
at diffedit3 369 lines 15 kB view raw
1// Copyright 2022 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::path::{self, Path, PathBuf}; 16 17use crate::common::{get_stderr_string, get_stdout_string, TestEnvironment}; 18 19fn set_up_non_empty_git_repo(git_repo: &git2::Repository) { 20 let signature = 21 git2::Signature::new("Some One", "some.one@example.com", &git2::Time::new(0, 0)).unwrap(); 22 let mut tree_builder = git_repo.treebuilder(None).unwrap(); 23 let file_oid = git_repo.blob(b"content").unwrap(); 24 tree_builder 25 .insert("file", file_oid, git2::FileMode::Blob.into()) 26 .unwrap(); 27 let tree_oid = tree_builder.write().unwrap(); 28 let tree = git_repo.find_tree(tree_oid).unwrap(); 29 git_repo 30 .commit( 31 Some("refs/heads/main"), 32 &signature, 33 &signature, 34 "message", 35 &tree, 36 &[], 37 ) 38 .unwrap(); 39 git_repo.set_head("refs/heads/main").unwrap(); 40} 41 42#[test] 43fn test_git_clone() { 44 let test_env = TestEnvironment::default(); 45 test_env.add_config("git.auto-local-branch = true"); 46 let git_repo_path = test_env.env_root().join("source"); 47 let git_repo = git2::Repository::init(git_repo_path).unwrap(); 48 49 // Clone an empty repo 50 let (stdout, stderr) = 51 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "empty"]); 52 insta::assert_snapshot!(stdout, @""); 53 insta::assert_snapshot!(stderr, @r###" 54 Fetching into new repo in "$TEST_ENV/empty" 55 Nothing changed. 56 "###); 57 58 set_up_non_empty_git_repo(&git_repo); 59 60 // Clone with relative source path 61 let (stdout, stderr) = 62 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone"]); 63 insta::assert_snapshot!(stdout, @""); 64 insta::assert_snapshot!(stderr, @r###" 65 Fetching into new repo in "$TEST_ENV/clone" 66 branch: main@origin [new] tracked 67 Working copy now at: uuqppmxq 1f0b881a (empty) (no description set) 68 Parent commit : mzyxwzks 9f01a0e0 main | message 69 Added 1 files, modified 0 files, removed 0 files 70 "###); 71 assert!(test_env.env_root().join("clone").join("file").exists()); 72 73 // Subsequent fetch should just work even if the source path was relative 74 let (stdout, stderr) = 75 test_env.jj_cmd_ok(&test_env.env_root().join("clone"), &["git", "fetch"]); 76 insta::assert_snapshot!(stdout, @""); 77 insta::assert_snapshot!(stderr, @r###" 78 Nothing changed. 79 "###); 80 81 // Failed clone should clean up the destination directory 82 std::fs::create_dir(test_env.env_root().join("bad")).unwrap(); 83 let assert = test_env 84 .jj_cmd(test_env.env_root(), &["git", "clone", "bad", "failed"]) 85 .assert() 86 .code(1); 87 let stdout = test_env.normalize_output(&get_stdout_string(&assert)); 88 let stderr = test_env.normalize_output(&get_stderr_string(&assert)); 89 insta::assert_snapshot!(stdout, @""); 90 insta::assert_snapshot!(stderr, @r###" 91 Fetching into new repo in "$TEST_ENV/failed" 92 Error: could not find repository at '$TEST_ENV/bad'; class=Repository (6) 93 "###); 94 assert!(!test_env.env_root().join("failed").exists()); 95 96 // Failed clone shouldn't remove the existing destination directory 97 std::fs::create_dir(test_env.env_root().join("failed")).unwrap(); 98 let assert = test_env 99 .jj_cmd(test_env.env_root(), &["git", "clone", "bad", "failed"]) 100 .assert() 101 .code(1); 102 let stdout = test_env.normalize_output(&get_stdout_string(&assert)); 103 let stderr = test_env.normalize_output(&get_stderr_string(&assert)); 104 insta::assert_snapshot!(stdout, @""); 105 insta::assert_snapshot!(stderr, @r###" 106 Fetching into new repo in "$TEST_ENV/failed" 107 Error: could not find repository at '$TEST_ENV/bad'; class=Repository (6) 108 "###); 109 assert!(test_env.env_root().join("failed").exists()); 110 assert!(!test_env.env_root().join("failed").join(".jj").exists()); 111 112 // Failed clone (if attempted) shouldn't remove the existing workspace 113 let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["git", "clone", "bad", "clone"]); 114 insta::assert_snapshot!(stderr, @r###" 115 Error: Destination path exists and is not an empty directory 116 "###); 117 assert!(test_env.env_root().join("clone").join(".jj").exists()); 118 119 // Try cloning into an existing workspace 120 let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["git", "clone", "source", "clone"]); 121 insta::assert_snapshot!(stderr, @r###" 122 Error: Destination path exists and is not an empty directory 123 "###); 124 125 // Try cloning into an existing file 126 std::fs::write(test_env.env_root().join("file"), "contents").unwrap(); 127 let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["git", "clone", "source", "file"]); 128 insta::assert_snapshot!(stderr, @r###" 129 Error: Destination path exists and is not an empty directory 130 "###); 131 132 // Try cloning into non-empty, non-workspace directory 133 std::fs::remove_dir_all(test_env.env_root().join("clone").join(".jj")).unwrap(); 134 let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["git", "clone", "source", "clone"]); 135 insta::assert_snapshot!(stderr, @r###" 136 Error: Destination path exists and is not an empty directory 137 "###); 138} 139 140#[test] 141fn test_git_clone_colocate() { 142 let test_env = TestEnvironment::default(); 143 test_env.add_config("git.auto-local-branch = true"); 144 let git_repo_path = test_env.env_root().join("source"); 145 let git_repo = git2::Repository::init(git_repo_path).unwrap(); 146 147 // Clone an empty repo 148 let (stdout, stderr) = test_env.jj_cmd_ok( 149 test_env.env_root(), 150 &["git", "clone", "source", "empty", "--colocate"], 151 ); 152 insta::assert_snapshot!(stdout, @""); 153 insta::assert_snapshot!(stderr, @r###" 154 Fetching into new repo in "$TEST_ENV/empty" 155 Nothing changed. 156 "###); 157 158 // git_target path should be relative to the store 159 let store_path = test_env 160 .env_root() 161 .join(PathBuf::from_iter(["empty", ".jj", "repo", "store"])); 162 let git_target_file_contents = std::fs::read_to_string(store_path.join("git_target")).unwrap(); 163 insta::assert_snapshot!( 164 git_target_file_contents.replace(path::MAIN_SEPARATOR, "/"), 165 @"../../../.git"); 166 167 set_up_non_empty_git_repo(&git_repo); 168 169 // Clone with relative source path 170 let (stdout, stderr) = test_env.jj_cmd_ok( 171 test_env.env_root(), 172 &["git", "clone", "source", "clone", "--colocate"], 173 ); 174 insta::assert_snapshot!(stdout, @""); 175 insta::assert_snapshot!(stderr, @r###" 176 Fetching into new repo in "$TEST_ENV/clone" 177 branch: main@origin [new] tracked 178 Working copy now at: uuqppmxq 1f0b881a (empty) (no description set) 179 Parent commit : mzyxwzks 9f01a0e0 main | message 180 Added 1 files, modified 0 files, removed 0 files 181 "###); 182 assert!(test_env.env_root().join("clone").join("file").exists()); 183 assert!(test_env.env_root().join("clone").join(".git").exists()); 184 185 eprintln!( 186 "{:?}", 187 git_repo.head().expect("Repo head should be set").name() 188 ); 189 190 let jj_git_repo = git2::Repository::open(test_env.env_root().join("clone")) 191 .expect("Could not open clone repo"); 192 assert_eq!( 193 jj_git_repo 194 .head() 195 .expect("Clone Repo HEAD should be set.") 196 .symbolic_target(), 197 git_repo 198 .head() 199 .expect("Repo HEAD should be set.") 200 .symbolic_target() 201 ); 202 // ".jj" directory should be ignored at Git side. 203 #[allow(clippy::format_collect)] 204 let git_statuses: String = jj_git_repo 205 .statuses(None) 206 .unwrap() 207 .iter() 208 .map(|entry| format!("{:?} {}\n", entry.status(), entry.path().unwrap())) 209 .collect(); 210 insta::assert_snapshot!(git_statuses, @r###" 211 Status(IGNORED) .jj/.gitignore 212 Status(IGNORED) .jj/repo/ 213 Status(IGNORED) .jj/working_copy/ 214 "###); 215 216 // The old default branch "master" shouldn't exist. 217 insta::assert_snapshot!( 218 get_branch_output(&test_env, &test_env.env_root().join("clone")), @r###" 219 main: mzyxwzks 9f01a0e0 message 220 @git: mzyxwzks 9f01a0e0 message 221 @origin: mzyxwzks 9f01a0e0 message 222 "###); 223 224 // Subsequent fetch should just work even if the source path was relative 225 let (stdout, stderr) = 226 test_env.jj_cmd_ok(&test_env.env_root().join("clone"), &["git", "fetch"]); 227 insta::assert_snapshot!(stdout, @""); 228 insta::assert_snapshot!(stderr, @r###" 229 Nothing changed. 230 "###); 231 232 // Failed clone should clean up the destination directory 233 std::fs::create_dir(test_env.env_root().join("bad")).unwrap(); 234 let assert = test_env 235 .jj_cmd( 236 test_env.env_root(), 237 &["git", "clone", "--colocate", "bad", "failed"], 238 ) 239 .assert() 240 .code(1); 241 let stdout = test_env.normalize_output(&get_stdout_string(&assert)); 242 let stderr = test_env.normalize_output(&get_stderr_string(&assert)); 243 insta::assert_snapshot!(stdout, @""); 244 insta::assert_snapshot!(stderr, @r###" 245 Fetching into new repo in "$TEST_ENV/failed" 246 Error: could not find repository at '$TEST_ENV/bad'; class=Repository (6) 247 "###); 248 assert!(!test_env.env_root().join("failed").exists()); 249 250 // Failed clone shouldn't remove the existing destination directory 251 std::fs::create_dir(test_env.env_root().join("failed")).unwrap(); 252 let assert = test_env 253 .jj_cmd( 254 test_env.env_root(), 255 &["git", "clone", "--colocate", "bad", "failed"], 256 ) 257 .assert() 258 .code(1); 259 let stdout = test_env.normalize_output(&get_stdout_string(&assert)); 260 let stderr = test_env.normalize_output(&get_stderr_string(&assert)); 261 insta::assert_snapshot!(stdout, @""); 262 insta::assert_snapshot!(stderr, @r###" 263 Fetching into new repo in "$TEST_ENV/failed" 264 Error: could not find repository at '$TEST_ENV/bad'; class=Repository (6) 265 "###); 266 assert!(test_env.env_root().join("failed").exists()); 267 assert!(!test_env.env_root().join("failed").join(".git").exists()); 268 assert!(!test_env.env_root().join("failed").join(".jj").exists()); 269 270 // Failed clone (if attempted) shouldn't remove the existing workspace 271 let stderr = test_env.jj_cmd_failure( 272 test_env.env_root(), 273 &["git", "clone", "--colocate", "bad", "clone"], 274 ); 275 insta::assert_snapshot!(stderr, @r###" 276 Error: Destination path exists and is not an empty directory 277 "###); 278 assert!(test_env.env_root().join("clone").join(".git").exists()); 279 assert!(test_env.env_root().join("clone").join(".jj").exists()); 280 281 // Try cloning into an existing workspace 282 let stderr = test_env.jj_cmd_failure( 283 test_env.env_root(), 284 &["git", "clone", "source", "clone", "--colocate"], 285 ); 286 insta::assert_snapshot!(stderr, @r###" 287 Error: Destination path exists and is not an empty directory 288 "###); 289 290 // Try cloning into an existing file 291 std::fs::write(test_env.env_root().join("file"), "contents").unwrap(); 292 let stderr = test_env.jj_cmd_failure( 293 test_env.env_root(), 294 &["git", "clone", "source", "file", "--colocate"], 295 ); 296 insta::assert_snapshot!(stderr, @r###" 297 Error: Destination path exists and is not an empty directory 298 "###); 299 300 // Try cloning into non-empty, non-workspace directory 301 std::fs::remove_dir_all(test_env.env_root().join("clone").join(".jj")).unwrap(); 302 let stderr = test_env.jj_cmd_failure( 303 test_env.env_root(), 304 &["git", "clone", "source", "clone", "--colocate"], 305 ); 306 insta::assert_snapshot!(stderr, @r###" 307 Error: Destination path exists and is not an empty directory 308 "###); 309} 310 311#[test] 312fn test_git_clone_remote_default_branch() { 313 let test_env = TestEnvironment::default(); 314 let git_repo_path = test_env.env_root().join("source"); 315 let git_repo = git2::Repository::init(git_repo_path).unwrap(); 316 set_up_non_empty_git_repo(&git_repo); 317 // Create non-default branch in remote 318 let oid = git_repo 319 .find_reference("refs/heads/main") 320 .unwrap() 321 .target() 322 .unwrap(); 323 git_repo 324 .reference("refs/heads/feature1", oid, false, "") 325 .unwrap(); 326 327 // All fetched branches will be imported if auto-local-branch is on 328 test_env.add_config("git.auto-local-branch = true"); 329 let (_stdout, stderr) = 330 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone1"]); 331 insta::assert_snapshot!(stderr, @r###" 332 Fetching into new repo in "$TEST_ENV/clone1" 333 branch: feature1@origin [new] tracked 334 branch: main@origin [new] tracked 335 Working copy now at: sqpuoqvx cad212e1 (empty) (no description set) 336 Parent commit : mzyxwzks 9f01a0e0 feature1 main | message 337 Added 1 files, modified 0 files, removed 0 files 338 "###); 339 insta::assert_snapshot!( 340 get_branch_output(&test_env, &test_env.env_root().join("clone1")), @r###" 341 feature1: mzyxwzks 9f01a0e0 message 342 @origin: mzyxwzks 9f01a0e0 message 343 main: mzyxwzks 9f01a0e0 message 344 @origin: mzyxwzks 9f01a0e0 message 345 "###); 346 347 // Only the default branch will be imported if auto-local-branch is off 348 test_env.add_config("git.auto-local-branch = false"); 349 let (_stdout, stderr) = 350 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone2"]); 351 insta::assert_snapshot!(stderr, @r###" 352 Fetching into new repo in "$TEST_ENV/clone2" 353 branch: feature1@origin [new] untracked 354 branch: main@origin [new] untracked 355 Working copy now at: pmmvwywv fa729b1e (empty) (no description set) 356 Parent commit : mzyxwzks 9f01a0e0 feature1@origin main | message 357 Added 1 files, modified 0 files, removed 0 files 358 "###); 359 insta::assert_snapshot!( 360 get_branch_output(&test_env, &test_env.env_root().join("clone2")), @r###" 361 feature1@origin: mzyxwzks 9f01a0e0 message 362 main: mzyxwzks 9f01a0e0 message 363 @origin: mzyxwzks 9f01a0e0 message 364 "###); 365} 366 367fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String { 368 test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes"]) 369}