just playing with tangled
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 common::TestEnvironment;
16
17pub mod common;
18
19#[test]
20fn test_diff_basic() {
21 let test_env = TestEnvironment::default();
22 test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
23 let repo_path = test_env.env_root().join("repo");
24
25 std::fs::write(repo_path.join("file1"), "foo\n").unwrap();
26 std::fs::write(repo_path.join("file2"), "foo\n").unwrap();
27 test_env.jj_cmd_success(&repo_path, &["new"]);
28 std::fs::remove_file(repo_path.join("file1")).unwrap();
29 std::fs::write(repo_path.join("file2"), "foo\nbar\n").unwrap();
30 std::fs::write(repo_path.join("file3"), "foo\n").unwrap();
31
32 let stdout = test_env.jj_cmd_success(&repo_path, &["diff"]);
33 insta::assert_snapshot!(stdout, @r###"
34 Removed regular file file1:
35 1 : foo
36 Modified regular file file2:
37 1 1: foo
38 2: bar
39 Added regular file file3:
40 1: foo
41 "###);
42
43 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]);
44 insta::assert_snapshot!(stdout, @r###"
45 R file1
46 M file2
47 A file3
48 "###);
49
50 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--types"]);
51 insta::assert_snapshot!(stdout, @r###"
52 F- file1
53 FF file2
54 -F file3
55 "###);
56
57 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--git"]);
58 insta::assert_snapshot!(stdout, @r###"
59 diff --git a/file1 b/file1
60 deleted file mode 100644
61 index 257cc5642c..0000000000
62 --- a/file1
63 +++ /dev/null
64 @@ -1,1 +1,0 @@
65 -foo
66 diff --git a/file2 b/file2
67 index 257cc5642c...3bd1f0e297 100644
68 --- a/file2
69 +++ b/file2
70 @@ -1,1 +1,2 @@
71 foo
72 +bar
73 diff --git a/file3 b/file3
74 new file mode 100644
75 index 0000000000..257cc5642c
76 --- /dev/null
77 +++ b/file3
78 @@ -1,0 +1,1 @@
79 +foo
80 "###);
81
82 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s", "--git"]);
83 insta::assert_snapshot!(stdout, @r###"
84 R file1
85 M file2
86 A file3
87 diff --git a/file1 b/file1
88 deleted file mode 100644
89 index 257cc5642c..0000000000
90 --- a/file1
91 +++ /dev/null
92 @@ -1,1 +1,0 @@
93 -foo
94 diff --git a/file2 b/file2
95 index 257cc5642c...3bd1f0e297 100644
96 --- a/file2
97 +++ b/file2
98 @@ -1,1 +1,2 @@
99 foo
100 +bar
101 diff --git a/file3 b/file3
102 new file mode 100644
103 index 0000000000..257cc5642c
104 --- /dev/null
105 +++ b/file3
106 @@ -1,0 +1,1 @@
107 +foo
108 "###);
109}
110
111#[test]
112fn test_diff_types() {
113 let test_env = TestEnvironment::default();
114 test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
115 let repo_path = test_env.env_root().join("repo");
116
117 let file_path = repo_path.join("foo");
118
119 // Missing
120 test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=missing"]);
121
122 // Normal file
123 test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=file"]);
124 std::fs::write(&file_path, "foo").unwrap();
125
126 // Conflict (add/add)
127 test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=conflict"]);
128 std::fs::write(&file_path, "foo").unwrap();
129 test_env.jj_cmd_success(&repo_path, &["new", "root"]);
130 std::fs::write(&file_path, "bar").unwrap();
131 test_env.jj_cmd_success(&repo_path, &["move", r#"--to=description("conflict")"#]);
132
133 #[cfg(unix)]
134 {
135 use std::os::unix::fs::PermissionsExt;
136 use std::path::PathBuf;
137
138 // Executable
139 test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=executable"]);
140 std::fs::write(&file_path, "foo").unwrap();
141 std::fs::set_permissions(&file_path, std::fs::Permissions::from_mode(0o755)).unwrap();
142
143 // Symlink
144 test_env.jj_cmd_success(&repo_path, &["new", "root", "-m=symlink"]);
145 std::os::unix::fs::symlink(PathBuf::from("."), &file_path).unwrap();
146 }
147
148 let diff = |from: &str, to: &str| {
149 test_env.jj_cmd_success(
150 &repo_path,
151 &[
152 "diff",
153 "--types",
154 &format!(r#"--from=description("{}")"#, from),
155 &format!(r#"--to=description("{}")"#, to),
156 ],
157 )
158 };
159 insta::assert_snapshot!(diff("missing", "file"), @r###"
160 -F foo
161 "###);
162 insta::assert_snapshot!(diff("file", "conflict"), @r###"
163 FC foo
164 "###);
165 insta::assert_snapshot!(diff("conflict", "missing"), @r###"
166 C- foo
167 "###);
168
169 #[cfg(unix)]
170 {
171 insta::assert_snapshot!(diff("symlink", "file"), @r###"
172 LF foo
173 "###);
174 insta::assert_snapshot!(diff("missing", "executable"), @r###"
175 -F foo
176 "###);
177 }
178}
179
180#[test]
181fn test_diff_bad_args() {
182 let test_env = TestEnvironment::default();
183 test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
184 let repo_path = test_env.env_root().join("repo");
185
186 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["diff", "-s", "--types"]);
187 insta::assert_snapshot!(stderr, @r###"
188 error: the argument '--summary' cannot be used with '--types'
189
190 Usage: jj diff --summary [PATHS]...
191
192 For more information, try '--help'.
193 "###);
194
195 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["diff", "--color-words", "--git"]);
196 insta::assert_snapshot!(stderr, @r###"
197 error: the argument '--color-words' cannot be used with '--git'
198
199 Usage: jj diff --color-words [PATHS]...
200
201 For more information, try '--help'.
202 "###);
203}
204
205#[test]
206fn test_diff_relative_paths() {
207 let test_env = TestEnvironment::default();
208 test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
209 let repo_path = test_env.env_root().join("repo");
210
211 std::fs::create_dir_all(repo_path.join("dir1").join("subdir1")).unwrap();
212 std::fs::create_dir(repo_path.join("dir2")).unwrap();
213 std::fs::write(repo_path.join("file1"), "foo1\n").unwrap();
214 std::fs::write(repo_path.join("dir1").join("file2"), "foo2\n").unwrap();
215 std::fs::write(
216 repo_path.join("dir1").join("subdir1").join("file3"),
217 "foo3\n",
218 )
219 .unwrap();
220 std::fs::write(repo_path.join("dir2").join("file4"), "foo4\n").unwrap();
221 test_env.jj_cmd_success(&repo_path, &["new"]);
222 std::fs::write(repo_path.join("file1"), "bar1\n").unwrap();
223 std::fs::write(repo_path.join("dir1").join("file2"), "bar2\n").unwrap();
224 std::fs::write(
225 repo_path.join("dir1").join("subdir1").join("file3"),
226 "bar3\n",
227 )
228 .unwrap();
229 std::fs::write(repo_path.join("dir2").join("file4"), "bar4\n").unwrap();
230
231 let stdout = test_env.jj_cmd_success(&repo_path.join("dir1"), &["diff"]);
232 #[cfg(unix)]
233 insta::assert_snapshot!(stdout, @r###"
234 Modified regular file file2:
235 1 1: foo2bar2
236 Modified regular file subdir1/file3:
237 1 1: foo3bar3
238 Modified regular file ../dir2/file4:
239 1 1: foo4bar4
240 Modified regular file ../file1:
241 1 1: foo1bar1
242 "###);
243 #[cfg(windows)]
244 insta::assert_snapshot!(stdout, @r###"
245 Modified regular file file2:
246 1 1: foo2bar2
247 Modified regular file subdir1\file3:
248 1 1: foo3bar3
249 Modified regular file ..\dir2\file4:
250 1 1: foo4bar4
251 Modified regular file ..\file1:
252 1 1: foo1bar1
253 "###);
254
255 let stdout = test_env.jj_cmd_success(&repo_path.join("dir1"), &["diff", "-s"]);
256 #[cfg(unix)]
257 insta::assert_snapshot!(stdout, @r###"
258 M file2
259 M subdir1/file3
260 M ../dir2/file4
261 M ../file1
262 "###);
263 #[cfg(windows)]
264 insta::assert_snapshot!(stdout, @r###"
265 M file2
266 M subdir1\file3
267 M ..\dir2\file4
268 M ..\file1
269 "###);
270
271 let stdout = test_env.jj_cmd_success(&repo_path.join("dir1"), &["diff", "--types"]);
272 #[cfg(unix)]
273 insta::assert_snapshot!(stdout, @r###"
274 FF file2
275 FF subdir1/file3
276 FF ../dir2/file4
277 FF ../file1
278 "###);
279 #[cfg(windows)]
280 insta::assert_snapshot!(stdout, @r###"
281 FF file2
282 FF subdir1\file3
283 FF ..\dir2\file4
284 FF ..\file1
285 "###);
286
287 let stdout = test_env.jj_cmd_success(&repo_path.join("dir1"), &["diff", "--git"]);
288 insta::assert_snapshot!(stdout, @r###"
289 diff --git a/dir1/file2 b/dir1/file2
290 index 54b060eee9...1fe912cdd8 100644
291 --- a/dir1/file2
292 +++ b/dir1/file2
293 @@ -1,1 +1,1 @@
294 -foo2
295 +bar2
296 diff --git a/dir1/subdir1/file3 b/dir1/subdir1/file3
297 index c1ec6c6f12...f3c8b75ec6 100644
298 --- a/dir1/subdir1/file3
299 +++ b/dir1/subdir1/file3
300 @@ -1,1 +1,1 @@
301 -foo3
302 +bar3
303 diff --git a/dir2/file4 b/dir2/file4
304 index a0016dbc4c...17375f7a12 100644
305 --- a/dir2/file4
306 +++ b/dir2/file4
307 @@ -1,1 +1,1 @@
308 -foo4
309 +bar4
310 diff --git a/file1 b/file1
311 index 1715acd6a5...05c4fe6772 100644
312 --- a/file1
313 +++ b/file1
314 @@ -1,1 +1,1 @@
315 -foo1
316 +bar1
317 "###);
318}
319
320#[test]
321fn test_diff_missing_newline() {
322 let test_env = TestEnvironment::default();
323 test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
324 let repo_path = test_env.env_root().join("repo");
325
326 std::fs::write(repo_path.join("file1"), "foo").unwrap();
327 std::fs::write(repo_path.join("file2"), "foo\nbar").unwrap();
328 test_env.jj_cmd_success(&repo_path, &["new"]);
329 std::fs::write(repo_path.join("file1"), "foo\nbar").unwrap();
330 std::fs::write(repo_path.join("file2"), "foo").unwrap();
331
332 let stdout = test_env.jj_cmd_success(&repo_path, &["diff"]);
333 insta::assert_snapshot!(stdout, @r###"
334 Modified regular file file1:
335 1 1: foo
336 2: bar
337 Modified regular file file2:
338 1 1: foo
339 2 : bar
340 "###);
341
342 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--git"]);
343 insta::assert_snapshot!(stdout, @r###"
344 diff --git a/file1 b/file1
345 index 1910281566...a907ec3f43 100644
346 --- a/file1
347 +++ b/file1
348 @@ -1,1 +1,2 @@
349 -foo
350 \ No newline at end of file
351 +foo
352 +bar
353 \ No newline at end of file
354 diff --git a/file2 b/file2
355 index a907ec3f43...1910281566 100644
356 --- a/file2
357 +++ b/file2
358 @@ -1,2 +1,1 @@
359 -foo
360 -bar
361 \ No newline at end of file
362 +foo
363 \ No newline at end of file
364 "###);
365}
366
367#[test]
368fn test_color_words_diff_missing_newline() {
369 let test_env = TestEnvironment::default();
370 test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
371 let repo_path = test_env.env_root().join("repo");
372
373 std::fs::write(repo_path.join("file1"), "").unwrap();
374 test_env.jj_cmd_success(&repo_path, &["commit", "-m", "=== Empty"]);
375 std::fs::write(repo_path.join("file1"), "a\nb\nc\nd\ne\nf\ng\nh\ni").unwrap();
376 test_env.jj_cmd_success(&repo_path, &["commit", "-m", "=== Add no newline"]);
377 std::fs::write(repo_path.join("file1"), "A\nb\nc\nd\ne\nf\ng\nh\ni").unwrap();
378 test_env.jj_cmd_success(&repo_path, &["commit", "-m", "=== Modify first line"]);
379 std::fs::write(repo_path.join("file1"), "A\nb\nc\nd\nE\nf\ng\nh\ni").unwrap();
380 test_env.jj_cmd_success(&repo_path, &["commit", "-m", "=== Modify middle line"]);
381 std::fs::write(repo_path.join("file1"), "A\nb\nc\nd\nE\nf\ng\nh\nI").unwrap();
382 test_env.jj_cmd_success(&repo_path, &["commit", "-m", "=== Modify last line"]);
383 std::fs::write(repo_path.join("file1"), "A\nb\nc\nd\nE\nf\ng\nh\nI\n").unwrap();
384 test_env.jj_cmd_success(&repo_path, &["commit", "-m", "=== Append newline"]);
385 std::fs::write(repo_path.join("file1"), "A\nb\nc\nd\nE\nf\ng\nh\nI").unwrap();
386 test_env.jj_cmd_success(&repo_path, &["commit", "-m", "=== Remove newline"]);
387 std::fs::write(repo_path.join("file1"), "").unwrap();
388 test_env.jj_cmd_success(&repo_path, &["commit", "-m", "=== Empty"]);
389
390 let stdout = test_env.jj_cmd_success(
391 &repo_path,
392 &[
393 "log",
394 "-Tdescription",
395 "-pr::@-",
396 "--no-graph",
397 "--reversed",
398 ],
399 );
400 insta::assert_snapshot!(stdout, @r###"
401 === Empty
402 Added regular file file1:
403 === Add no newline
404 Modified regular file file1:
405 1: a
406 2: b
407 3: c
408 4: d
409 5: e
410 6: f
411 7: g
412 8: h
413 9: i
414 === Modify first line
415 Modified regular file file1:
416 1 1: aA
417 2 2: b
418 3 3: c
419 4 4: d
420 ...
421 === Modify middle line
422 Modified regular file file1:
423 1 1: A
424 2 2: b
425 3 3: c
426 4 4: d
427 5 5: eE
428 6 6: f
429 7 7: g
430 8 8: h
431 9 9: i
432 === Modify last line
433 Modified regular file file1:
434 ...
435 6 6: f
436 7 7: g
437 8 8: h
438 9 9: iI
439 === Append newline
440 Modified regular file file1:
441 ...
442 6 6: f
443 7 7: g
444 8 8: h
445 9 9: I
446 === Remove newline
447 Modified regular file file1:
448 ...
449 6 6: f
450 7 7: g
451 8 8: h
452 9 9: I
453 === Empty
454 Modified regular file file1:
455 1 : A
456 2 : b
457 3 : c
458 4 : d
459 5 : E
460 6 : f
461 7 : g
462 8 : h
463 9 : I
464 "###);
465}
466
467#[test]
468fn test_diff_skipped_context() {
469 let test_env = TestEnvironment::default();
470 test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
471 let repo_path = test_env.env_root().join("repo");
472
473 std::fs::write(repo_path.join("file1"), "a\nb\nc\nd\ne\nf\ng\nh\ni\nj").unwrap();
474 test_env.jj_cmd_success(&repo_path, &["describe", "-m", "=== Left side of diffs"]);
475
476 test_env.jj_cmd_success(&repo_path, &["new", "@", "-m", "=== Must skip 2 lines"]);
477 std::fs::write(repo_path.join("file1"), "A\nb\nc\nd\ne\nf\ng\nh\ni\nJ").unwrap();
478 test_env.jj_cmd_success(&repo_path, &["new", "@-", "-m", "=== Don't skip 1 line"]);
479 std::fs::write(repo_path.join("file1"), "A\nb\nc\nd\ne\nf\ng\nh\nI\nj").unwrap();
480 test_env.jj_cmd_success(&repo_path, &["new", "@-", "-m", "=== No gap to skip"]);
481 std::fs::write(repo_path.join("file1"), "a\nB\nc\nd\ne\nf\ng\nh\nI\nj").unwrap();
482 test_env.jj_cmd_success(&repo_path, &["new", "@-", "-m", "=== No gap to skip"]);
483 std::fs::write(repo_path.join("file1"), "a\nb\nC\nd\ne\nf\ng\nh\nI\nj").unwrap();
484 test_env.jj_cmd_success(&repo_path, &["new", "@-", "-m", "=== 1 line at start"]);
485 std::fs::write(repo_path.join("file1"), "a\nb\nc\nd\nE\nf\ng\nh\ni\nj").unwrap();
486 test_env.jj_cmd_success(&repo_path, &["new", "@-", "-m", "=== 1 line at end"]);
487 std::fs::write(repo_path.join("file1"), "a\nb\nc\nd\ne\nF\ng\nh\ni\nj").unwrap();
488
489 let stdout = test_env.jj_cmd_success(
490 &repo_path,
491 &["log", "-Tdescription", "-p", "--no-graph", "--reversed"],
492 );
493 insta::assert_snapshot!(stdout, @r###"
494 === Left side of diffs
495 Added regular file file1:
496 1: a
497 2: b
498 3: c
499 4: d
500 5: e
501 6: f
502 7: g
503 8: h
504 9: i
505 10: j
506 === Must skip 2 lines
507 Modified regular file file1:
508 1 1: aA
509 2 2: b
510 3 3: c
511 4 4: d
512 ...
513 7 7: g
514 8 8: h
515 9 9: i
516 10 10: jJ
517 === Don't skip 1 line
518 Modified regular file file1:
519 1 1: aA
520 2 2: b
521 3 3: c
522 4 4: d
523 5 5: e
524 6 6: f
525 7 7: g
526 8 8: h
527 9 9: iI
528 10 10: j
529 === No gap to skip
530 Modified regular file file1:
531 1 1: a
532 2 2: bB
533 3 3: c
534 4 4: d
535 5 5: e
536 6 6: f
537 7 7: g
538 8 8: h
539 9 9: iI
540 10 10: j
541 === No gap to skip
542 Modified regular file file1:
543 1 1: a
544 2 2: b
545 3 3: cC
546 4 4: d
547 5 5: e
548 6 6: f
549 7 7: g
550 8 8: h
551 9 9: iI
552 10 10: j
553 === 1 line at start
554 Modified regular file file1:
555 1 1: a
556 2 2: b
557 3 3: c
558 4 4: d
559 5 5: eE
560 6 6: f
561 7 7: g
562 8 8: h
563 ...
564 === 1 line at end
565 Modified regular file file1:
566 ...
567 3 3: c
568 4 4: d
569 5 5: e
570 6 6: fF
571 7 7: g
572 8 8: h
573 9 9: i
574 10 10: j
575 "###);
576}