just playing with tangled
at ig/vimdiffwarn 463 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::fs; 16use std::io::Write as _; 17use std::path::Path; 18use std::path::PathBuf; 19 20use testutils::git; 21 22use crate::common::TestEnvironment; 23 24fn read_git_config(repo_path: &Path) -> String { 25 let git_config = fs::read_to_string(repo_path.join(".jj/repo/store/git/config")) 26 .or_else(|_| fs::read_to_string(repo_path.join(".git/config"))) 27 .unwrap(); 28 git_config 29 .split_inclusive('\n') 30 .filter(|line| { 31 // Filter out non‐portable values. 32 [ 33 "\tfilemode =", 34 "\tsymlinks =", 35 "\tignorecase =", 36 "\tprecomposeunicode =", 37 ] 38 .iter() 39 .all(|prefix| !line.to_ascii_lowercase().starts_with(prefix)) 40 }) 41 .collect() 42} 43 44#[test] 45fn test_git_remotes() { 46 let test_env = TestEnvironment::default(); 47 48 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 49 let work_dir = test_env.work_dir("repo"); 50 51 let output = work_dir.run_jj(["git", "remote", "list"]); 52 insta::assert_snapshot!(output, @""); 53 let output = work_dir.run_jj(["git", "remote", "add", "foo", "http://example.com/repo/foo"]); 54 insta::assert_snapshot!(output, @""); 55 let output = work_dir.run_jj(["git", "remote", "add", "bar", "http://example.com/repo/bar"]); 56 insta::assert_snapshot!(output, @""); 57 let output = work_dir.run_jj(["git", "remote", "list"]); 58 insta::assert_snapshot!(output, @r" 59 bar http://example.com/repo/bar 60 foo http://example.com/repo/foo 61 [EOF] 62 "); 63 let output = work_dir.run_jj(["git", "remote", "remove", "foo"]); 64 insta::assert_snapshot!(output, @""); 65 let output = work_dir.run_jj(["git", "remote", "list"]); 66 insta::assert_snapshot!(output, @r" 67 bar http://example.com/repo/bar 68 [EOF] 69 "); 70 let output = work_dir.run_jj(["git", "remote", "remove", "nonexistent"]); 71 insta::assert_snapshot!(output, @r" 72 ------- stderr ------- 73 Error: No git remote named 'nonexistent' 74 [EOF] 75 [exit status: 1] 76 "); 77 insta::assert_snapshot!(read_git_config(work_dir.root()), @r#" 78 [core] 79 repositoryformatversion = 0 80 bare = true 81 logallrefupdates = false 82 [remote "bar"] 83 url = http://example.com/repo/bar 84 fetch = +refs/heads/*:refs/remotes/bar/* 85 "#); 86} 87 88#[test] 89fn test_git_remote_add() { 90 let test_env = TestEnvironment::default(); 91 92 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 93 let work_dir = test_env.work_dir("repo"); 94 work_dir 95 .run_jj(["git", "remote", "add", "foo", "http://example.com/repo/foo"]) 96 .success(); 97 let output = work_dir.run_jj([ 98 "git", 99 "remote", 100 "add", 101 "foo", 102 "http://example.com/repo/foo2", 103 ]); 104 insta::assert_snapshot!(output, @r" 105 ------- stderr ------- 106 Error: Git remote named 'foo' already exists 107 [EOF] 108 [exit status: 1] 109 "); 110 let output = work_dir.run_jj(["git", "remote", "add", "git", "http://example.com/repo/git"]); 111 insta::assert_snapshot!(output, @r" 112 ------- stderr ------- 113 Error: Git remote named 'git' is reserved for local Git repository 114 [EOF] 115 [exit status: 1] 116 "); 117 let output = work_dir.run_jj(["git", "remote", "list"]); 118 insta::assert_snapshot!(output, @r" 119 foo http://example.com/repo/foo 120 [EOF] 121 "); 122} 123 124#[test] 125fn test_git_remote_set_url() { 126 let test_env = TestEnvironment::default(); 127 128 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 129 let work_dir = test_env.work_dir("repo"); 130 work_dir 131 .run_jj(["git", "remote", "add", "foo", "http://example.com/repo/foo"]) 132 .success(); 133 let output = work_dir.run_jj([ 134 "git", 135 "remote", 136 "set-url", 137 "bar", 138 "http://example.com/repo/bar", 139 ]); 140 insta::assert_snapshot!(output, @r" 141 ------- stderr ------- 142 Error: No git remote named 'bar' 143 [EOF] 144 [exit status: 1] 145 "); 146 let output = work_dir.run_jj([ 147 "git", 148 "remote", 149 "set-url", 150 "git", 151 "http://example.com/repo/git", 152 ]); 153 insta::assert_snapshot!(output, @r" 154 ------- stderr ------- 155 Error: Git remote named 'git' is reserved for local Git repository 156 [EOF] 157 [exit status: 1] 158 "); 159 let output = work_dir.run_jj([ 160 "git", 161 "remote", 162 "set-url", 163 "foo", 164 "http://example.com/repo/bar", 165 ]); 166 insta::assert_snapshot!(output, @""); 167 let output = work_dir.run_jj(["git", "remote", "list"]); 168 insta::assert_snapshot!(output, @r" 169 foo http://example.com/repo/bar 170 [EOF] 171 "); 172 insta::assert_snapshot!(read_git_config(work_dir.root()), @r#" 173 [core] 174 repositoryformatversion = 0 175 bare = true 176 logallrefupdates = false 177 [remote "foo"] 178 url = http://example.com/repo/bar 179 fetch = +refs/heads/*:refs/remotes/foo/* 180 "#); 181} 182 183#[test] 184fn test_git_remote_relative_path() { 185 let test_env = TestEnvironment::default(); 186 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 187 let work_dir = test_env.work_dir("repo"); 188 189 // Relative path using OS-native separator 190 let path = PathBuf::from_iter(["..", "native", "sep"]); 191 work_dir 192 .run_jj(["git", "remote", "add", "foo", path.to_str().unwrap()]) 193 .success(); 194 let output = work_dir.run_jj(["git", "remote", "list"]); 195 insta::assert_snapshot!(output, @r" 196 foo $TEST_ENV/native/sep 197 [EOF] 198 "); 199 200 // Relative path using UNIX separator 201 test_env 202 .run_jj_in( 203 ".", 204 ["-Rrepo", "git", "remote", "set-url", "foo", "unix/sep"], 205 ) 206 .success(); 207 let output = work_dir.run_jj(["git", "remote", "list"]); 208 insta::assert_snapshot!(output, @r" 209 foo $TEST_ENV/unix/sep 210 [EOF] 211 "); 212} 213 214#[test] 215fn test_git_remote_rename() { 216 let test_env = TestEnvironment::default(); 217 218 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 219 let work_dir = test_env.work_dir("repo"); 220 work_dir 221 .run_jj(["git", "remote", "add", "foo", "http://example.com/repo/foo"]) 222 .success(); 223 work_dir 224 .run_jj(["git", "remote", "add", "baz", "http://example.com/repo/baz"]) 225 .success(); 226 let output = work_dir.run_jj(["git", "remote", "rename", "bar", "foo"]); 227 insta::assert_snapshot!(output, @r" 228 ------- stderr ------- 229 Error: No git remote named 'bar' 230 [EOF] 231 [exit status: 1] 232 "); 233 let output = work_dir.run_jj(["git", "remote", "rename", "foo", "baz"]); 234 insta::assert_snapshot!(output, @r" 235 ------- stderr ------- 236 Error: Git remote named 'baz' already exists 237 [EOF] 238 [exit status: 1] 239 "); 240 let output = work_dir.run_jj(["git", "remote", "rename", "foo", "git"]); 241 insta::assert_snapshot!(output, @r" 242 ------- stderr ------- 243 Error: Git remote named 'git' is reserved for local Git repository 244 [EOF] 245 [exit status: 1] 246 "); 247 let output = work_dir.run_jj(["git", "remote", "rename", "foo", "bar"]); 248 insta::assert_snapshot!(output, @""); 249 let output = work_dir.run_jj(["git", "remote", "list"]); 250 insta::assert_snapshot!(output, @r" 251 bar http://example.com/repo/foo 252 baz http://example.com/repo/baz 253 [EOF] 254 "); 255 insta::assert_snapshot!(read_git_config(work_dir.root()), @r#" 256 [core] 257 repositoryformatversion = 0 258 bare = true 259 logallrefupdates = false 260 [remote "baz"] 261 url = http://example.com/repo/baz 262 fetch = +refs/heads/*:refs/remotes/baz/* 263 [remote "bar"] 264 url = http://example.com/repo/foo 265 fetch = +refs/heads/*:refs/remotes/bar/* 266 "#); 267} 268 269#[test] 270fn test_git_remote_named_git() { 271 let test_env = TestEnvironment::default(); 272 273 // Existing remote named 'git' shouldn't block the repo initialization. 274 let work_dir = test_env.work_dir("repo"); 275 git::init(work_dir.root()); 276 git::add_remote(work_dir.root(), "git", "http://example.com/repo/repo"); 277 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 278 work_dir 279 .run_jj(["bookmark", "create", "-r@", "main"]) 280 .success(); 281 282 // The remote can be renamed. 283 let output = work_dir.run_jj(["git", "remote", "rename", "git", "bar"]); 284 insta::assert_snapshot!(output, @""); 285 let output = work_dir.run_jj(["git", "remote", "list"]); 286 insta::assert_snapshot!(output, @r" 287 bar http://example.com/repo/repo 288 [EOF] 289 "); 290 insta::assert_snapshot!(read_git_config(work_dir.root()), @r#" 291 [core] 292 repositoryformatversion = 0 293 bare = false 294 logallrefupdates = true 295 [remote "bar"] 296 url = http://example.com/repo/repo 297 fetch = +refs/heads/*:refs/remotes/bar/* 298 "#); 299 // @git bookmark shouldn't be renamed. 300 let output = work_dir.run_jj(["log", "-rmain@git", "-Tbookmarks"]); 301 insta::assert_snapshot!(output, @r" 302 @ main 303304 ~ 305 [EOF] 306 "); 307 308 // The remote cannot be renamed back by jj. 309 let output = work_dir.run_jj(["git", "remote", "rename", "bar", "git"]); 310 insta::assert_snapshot!(output, @r" 311 ------- stderr ------- 312 Error: Git remote named 'git' is reserved for local Git repository 313 [EOF] 314 [exit status: 1] 315 "); 316 317 // Reinitialize the repo with remote named 'git'. 318 work_dir.remove_dir_all(".jj"); 319 git::rename_remote(work_dir.root(), "bar", "git"); 320 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 321 insta::assert_snapshot!(read_git_config(work_dir.root()), @r#" 322 [core] 323 repositoryformatversion = 0 324 bare = false 325 logallrefupdates = true 326 [remote "git"] 327 url = http://example.com/repo/repo 328 fetch = +refs/heads/*:refs/remotes/git/* 329 "#); 330 331 // The remote can also be removed. 332 let output = work_dir.run_jj(["git", "remote", "remove", "git"]); 333 insta::assert_snapshot!(output, @""); 334 let output = work_dir.run_jj(["git", "remote", "list"]); 335 insta::assert_snapshot!(output, @""); 336 insta::assert_snapshot!(read_git_config(work_dir.root()), @r#" 337 [core] 338 repositoryformatversion = 0 339 bare = false 340 logallrefupdates = true 341 "#); 342 // @git bookmark shouldn't be removed. 343 let output = work_dir.run_jj(["log", "-rmain@git", "-Tbookmarks"]); 344 insta::assert_snapshot!(output, @r" 345 ○ main 346347 ~ 348 [EOF] 349 "); 350} 351 352#[test] 353fn test_git_remote_with_slashes() { 354 let test_env = TestEnvironment::default(); 355 356 // Existing remote with slashes shouldn't block the repo initialization. 357 let work_dir = test_env.work_dir("repo"); 358 git::init(work_dir.root()); 359 git::add_remote( 360 work_dir.root(), 361 "slash/origin", 362 "http://example.com/repo/repo", 363 ); 364 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 365 work_dir 366 .run_jj(["bookmark", "create", "-r@", "main"]) 367 .success(); 368 369 // Cannot add remote with a slash via `jj` 370 let output = work_dir.run_jj([ 371 "git", 372 "remote", 373 "add", 374 "another/origin", 375 "http://examples.org/repo/repo", 376 ]); 377 insta::assert_snapshot!(output, @r" 378 ------- stderr ------- 379 Error: Git remotes with slashes are incompatible with jj: another/origin 380 [EOF] 381 [exit status: 1] 382 "); 383 let output = work_dir.run_jj(["git", "remote", "list"]); 384 insta::assert_snapshot!(output, @r" 385 slash/origin http://example.com/repo/repo 386 [EOF] 387 "); 388 389 // The remote can be renamed. 390 let output = work_dir.run_jj(["git", "remote", "rename", "slash/origin", "origin"]); 391 insta::assert_snapshot!(output, @""); 392 let output = work_dir.run_jj(["git", "remote", "list"]); 393 insta::assert_snapshot!(output, @r" 394 origin http://example.com/repo/repo 395 [EOF] 396 "); 397 398 // The remote cannot be renamed back by jj. 399 let output = work_dir.run_jj(["git", "remote", "rename", "origin", "slash/origin"]); 400 insta::assert_snapshot!(output, @r" 401 ------- stderr ------- 402 Error: Git remotes with slashes are incompatible with jj: slash/origin 403 [EOF] 404 [exit status: 1] 405 "); 406 407 // Reinitialize the repo with remote with slashes 408 work_dir.remove_dir_all(".jj"); 409 git::rename_remote(work_dir.root(), "origin", "slash/origin"); 410 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 411 412 // The remote can also be removed. 413 let output = work_dir.run_jj(["git", "remote", "remove", "slash/origin"]); 414 insta::assert_snapshot!(output, @""); 415 let output = work_dir.run_jj(["git", "remote", "list"]); 416 insta::assert_snapshot!(output, @""); 417 // @git bookmark shouldn't be removed. 418 let output = work_dir.run_jj(["log", "-rmain@git", "-Tbookmarks"]); 419 insta::assert_snapshot!(output, @r" 420 ○ main 421422 ~ 423 [EOF] 424 "); 425} 426 427#[test] 428fn test_git_remote_with_branch_config() { 429 let test_env = TestEnvironment::default(); 430 431 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 432 let work_dir = test_env.work_dir("repo"); 433 434 let output = work_dir.run_jj(["git", "remote", "add", "foo", "http://example.com/repo"]); 435 insta::assert_snapshot!(output, @""); 436 437 let mut config_file = fs::OpenOptions::new() 438 .append(true) 439 .open(work_dir.root().join(".jj/repo/store/git/config")) 440 .unwrap(); 441 // `git clone` adds branch configuration like this. 442 let eol = if cfg!(windows) { "\r\n" } else { "\n" }; 443 write!(config_file, "[branch \"test\"]{eol}").unwrap(); 444 write!(config_file, "\tremote = foo{eol}").unwrap(); 445 write!(config_file, "\tmerge = refs/heads/test{eol}").unwrap(); 446 drop(config_file); 447 448 let output = work_dir.run_jj(["git", "remote", "rename", "foo", "bar"]); 449 insta::assert_snapshot!(output, @""); 450 451 insta::assert_snapshot!(read_git_config(work_dir.root()), @r#" 452 [core] 453 repositoryformatversion = 0 454 bare = true 455 logallrefupdates = false 456 [branch "test"] 457 remote = bar 458 merge = refs/heads/test 459 [remote "bar"] 460 url = http://example.com/repo 461 fetch = +refs/heads/*:refs/remotes/bar/* 462 "#); 463}