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 std::path::PathBuf;
16
17use crate::common::CommandOutput;
18use crate::common::TestEnvironment;
19use crate::common::TestWorkDir;
20
21#[test]
22fn test_commit_with_description_from_cli() {
23 let test_env = TestEnvironment::default();
24 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
25 let work_dir = test_env.work_dir("repo");
26
27 // Description applies to the current working-copy (not the new one)
28 work_dir.run_jj(["commit", "-m=first"]).success();
29 insta::assert_snapshot!(get_log_output(&work_dir), @r"
30 @ e8ea92a8b6b3
31 ○ fa15625b4a98 first
32 ◆ 000000000000
33 [EOF]
34 ");
35}
36
37#[test]
38fn test_commit_with_editor() {
39 let mut test_env = TestEnvironment::default();
40 let edit_script = test_env.set_up_fake_editor();
41 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
42 let work_dir = test_env.work_dir("repo");
43
44 // Check that the text file gets initialized with the current description and
45 // set a new one
46 work_dir.run_jj(["describe", "-m=initial"]).success();
47 std::fs::write(&edit_script, ["dump editor0", "write\nmodified"].join("\0")).unwrap();
48 work_dir.run_jj(["commit"]).success();
49 insta::assert_snapshot!(get_log_output(&work_dir), @r"
50 @ a57b2c95fb75
51 ○ 159271101e05 modified
52 ◆ 000000000000
53 [EOF]
54 ");
55 insta::assert_snapshot!(
56 std::fs::read_to_string(test_env.env_root().join("editor0")).unwrap(), @r#"
57 initial
58
59 JJ: Lines starting with "JJ:" (like this one) will be removed.
60 "#);
61
62 // Check that the editor content includes diff summary
63 work_dir.write_file("file1", "foo\n");
64 work_dir.write_file("file2", "foo\n");
65 work_dir.run_jj(["describe", "-m=add files"]).success();
66 std::fs::write(&edit_script, "dump editor1").unwrap();
67 work_dir.run_jj(["commit"]).success();
68 insta::assert_snapshot!(
69 std::fs::read_to_string(test_env.env_root().join("editor1")).unwrap(), @r#"
70 add files
71
72 JJ: This commit contains the following changes:
73 JJ: A file1
74 JJ: A file2
75 JJ:
76 JJ: Lines starting with "JJ:" (like this one) will be removed.
77 "#);
78}
79
80#[test]
81fn test_commit_with_editor_avoids_unc() {
82 let mut test_env = TestEnvironment::default();
83 let edit_script = test_env.set_up_fake_editor();
84 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
85 let work_dir = test_env.work_dir("repo");
86
87 std::fs::write(edit_script, "dump-path path").unwrap();
88 work_dir.run_jj(["commit"]).success();
89
90 let edited_path =
91 PathBuf::from(std::fs::read_to_string(test_env.env_root().join("path")).unwrap());
92 // While `assert!(!edited_path.starts_with("//?/"))` could work here in most
93 // cases, it fails when it is not safe to strip the prefix, such as paths
94 // over 260 chars.
95 assert_eq!(edited_path, dunce::simplified(&edited_path));
96}
97
98#[test]
99fn test_commit_interactive() {
100 let mut test_env = TestEnvironment::default();
101 let edit_script = test_env.set_up_fake_editor();
102 let diff_editor = test_env.set_up_fake_diff_editor();
103 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
104 let work_dir = test_env.work_dir("repo");
105
106 work_dir.write_file("file1", "foo\n");
107 work_dir.write_file("file2", "bar\n");
108 work_dir.run_jj(["describe", "-m=add files"]).success();
109 std::fs::write(edit_script, ["dump editor"].join("\0")).unwrap();
110
111 let diff_script = ["rm file2", "dump JJ-INSTRUCTIONS instrs"].join("\0");
112 std::fs::write(diff_editor, diff_script).unwrap();
113
114 // Create a commit interactively and select only file1
115 work_dir.run_jj(["commit", "-i"]).success();
116
117 insta::assert_snapshot!(
118 std::fs::read_to_string(test_env.env_root().join("instrs")).unwrap(), @r"
119 You are splitting the working-copy commit: qpvuntsm 4219467e add files
120
121 The diff initially shows all changes. Adjust the right side until it shows the
122 contents you want for the first commit. The remainder will be included in the
123 new working-copy commit.
124 ");
125
126 insta::assert_snapshot!(
127 std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#"
128 add files
129
130 JJ: This commit contains the following changes:
131 JJ: A file1
132 JJ:
133 JJ: Lines starting with "JJ:" (like this one) will be removed.
134 "#);
135
136 // Try again with --tool=<name>, which implies --interactive
137 work_dir.run_jj(["undo"]).success();
138 work_dir
139 .run_jj([
140 "commit",
141 "--config=ui.diff-editor='false'",
142 "--tool=fake-diff-editor",
143 ])
144 .success();
145
146 insta::assert_snapshot!(
147 std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#"
148 add files
149
150 JJ: This commit contains the following changes:
151 JJ: A file1
152 JJ:
153 JJ: Lines starting with "JJ:" (like this one) will be removed.
154 "#);
155
156 let output = work_dir.run_jj(["log", "--summary"]);
157 insta::assert_snapshot!(output, @r"
158 @ mzvwutvl test.user@example.com 2001-02-03 08:05:11 21b846a6
159 │ (no description set)
160 │ A file2
161 ○ qpvuntsm test.user@example.com 2001-02-03 08:05:11 7d156390
162 │ add files
163 │ A file1
164 ◆ zzzzzzzz root() 00000000
165 [EOF]
166 ");
167}
168
169#[test]
170fn test_commit_interactive_with_paths() {
171 let mut test_env = TestEnvironment::default();
172 let edit_script = test_env.set_up_fake_editor();
173 let diff_editor = test_env.set_up_fake_diff_editor();
174 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
175 let work_dir = test_env.work_dir("repo");
176
177 work_dir.write_file("file2", "");
178 work_dir.write_file("file3", "");
179 work_dir.run_jj(["new", "-medit"]).success();
180 work_dir.write_file("file1", "foo\n");
181 work_dir.write_file("file2", "bar\n");
182 work_dir.write_file("file3", "baz\n");
183
184 std::fs::write(edit_script, ["dump editor"].join("\0")).unwrap();
185 let diff_script = [
186 "files-before file2",
187 "files-after JJ-INSTRUCTIONS file1 file2",
188 "reset file2",
189 ]
190 .join("\0");
191 std::fs::write(diff_editor, diff_script).unwrap();
192
193 // Select file1 and file2 by args, then select file1 interactively
194 let output = work_dir.run_jj(["commit", "-i", "file1", "file2"]);
195 insta::assert_snapshot!(output, @r"
196 ------- stderr -------
197 Working copy (@) now at: kkmpptxz f3e6062e (no description set)
198 Parent commit (@-) : rlvkpnrz 9453cb28 edit
199 [EOF]
200 ");
201
202 insta::assert_snapshot!(
203 std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#"
204 edit
205
206 JJ: This commit contains the following changes:
207 JJ: A file1
208 JJ:
209 JJ: Lines starting with "JJ:" (like this one) will be removed.
210 "#);
211
212 let output = work_dir.run_jj(["log", "--summary"]);
213 insta::assert_snapshot!(output, @r"
214 @ kkmpptxz test.user@example.com 2001-02-03 08:05:09 f3e6062e
215 │ (no description set)
216 │ M file2
217 │ M file3
218 ○ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 9453cb28
219 │ edit
220 │ A file1
221 ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 497ed465
222 │ (no description set)
223 │ A file2
224 │ A file3
225 ◆ zzzzzzzz root() 00000000
226 [EOF]
227 ");
228}
229
230#[test]
231fn test_commit_with_default_description() {
232 let mut test_env = TestEnvironment::default();
233 let edit_script = test_env.set_up_fake_editor();
234 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
235 test_env.add_config(r#"ui.default-description = "\n\nTESTED=TODO""#);
236 let work_dir = test_env.work_dir("repo");
237
238 work_dir.write_file("file1", "foo\n");
239 work_dir.write_file("file2", "bar\n");
240 std::fs::write(edit_script, ["dump editor"].join("\0")).unwrap();
241 work_dir.run_jj(["commit"]).success();
242
243 insta::assert_snapshot!(get_log_output(&work_dir), @r"
244 @ c65242099289
245 ○ 573b6df51aea TESTED=TODO
246 ◆ 000000000000
247 [EOF]
248 ");
249 insta::assert_snapshot!(
250 std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#"
251 TESTED=TODO
252
253 JJ: This commit contains the following changes:
254 JJ: A file1
255 JJ: A file2
256 JJ:
257 JJ: Lines starting with "JJ:" (like this one) will be removed.
258 "#);
259}
260
261#[test]
262fn test_commit_with_description_template() {
263 let mut test_env = TestEnvironment::default();
264 let edit_script = test_env.set_up_fake_editor();
265 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
266 test_env.add_config(
267 r#"
268 [templates]
269 draft_commit_description = '''
270 concat(
271 description,
272 "\n",
273 indent(
274 "JJ: ",
275 concat(
276 "Author: " ++ format_detailed_signature(author) ++ "\n",
277 "Committer: " ++ format_detailed_signature(committer) ++ "\n",
278 "\n",
279 diff.stat(76),
280 ),
281 ),
282 )
283 '''
284 "#,
285 );
286 let work_dir = test_env.work_dir("repo");
287
288 std::fs::write(edit_script, ["dump editor"].join("\0")).unwrap();
289
290 work_dir.write_file("file1", "foo\n");
291 work_dir.write_file("file2", "bar\n");
292 work_dir.write_file("file3", "foobar\n");
293
294 // Only file1 should be included in the diff
295 work_dir.run_jj(["commit", "file1"]).success();
296 insta::assert_snapshot!(
297 std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#"
298 JJ: Author: Test User <test.user@example.com> (2001-02-03 08:05:08)
299 JJ: Committer: Test User <test.user@example.com> (2001-02-03 08:05:08)
300
301 JJ: file1 | 1 +
302 JJ: 1 file changed, 1 insertion(+), 0 deletions(-)
303 JJ:
304 JJ: Lines starting with "JJ:" (like this one) will be removed.
305 "#);
306
307 // Only file2 with modified author should be included in the diff
308 work_dir
309 .run_jj([
310 "commit",
311 "--author",
312 "Another User <another.user@example.com>",
313 "file2",
314 ])
315 .success();
316 insta::assert_snapshot!(
317 std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#"
318 JJ: Author: Another User <another.user@example.com> (2001-02-03 08:05:08)
319 JJ: Committer: Test User <test.user@example.com> (2001-02-03 08:05:09)
320
321 JJ: file2 | 1 +
322 JJ: 1 file changed, 1 insertion(+), 0 deletions(-)
323 JJ:
324 JJ: Lines starting with "JJ:" (like this one) will be removed.
325 "#);
326
327 // Timestamp after the reset should be available to the template
328 work_dir.run_jj(["commit", "--reset-author"]).success();
329 insta::assert_snapshot!(
330 std::fs::read_to_string(test_env.env_root().join("editor")).unwrap(), @r#"
331 JJ: Author: Test User <test.user@example.com> (2001-02-03 08:05:10)
332 JJ: Committer: Test User <test.user@example.com> (2001-02-03 08:05:10)
333
334 JJ: file3 | 1 +
335 JJ: 1 file changed, 1 insertion(+), 0 deletions(-)
336 JJ:
337 JJ: Lines starting with "JJ:" (like this one) will be removed.
338 "#);
339}
340
341#[test]
342fn test_commit_without_working_copy() {
343 let test_env = TestEnvironment::default();
344 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
345 let work_dir = test_env.work_dir("repo");
346
347 work_dir.run_jj(["workspace", "forget"]).success();
348 let output = work_dir.run_jj(["commit", "-m=first"]);
349 insta::assert_snapshot!(output, @r"
350 ------- stderr -------
351 Error: This command requires a working copy
352 [EOF]
353 [exit status: 1]
354 ");
355}
356
357#[test]
358fn test_commit_paths() {
359 let test_env = TestEnvironment::default();
360 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
361 let work_dir = test_env.work_dir("repo");
362
363 work_dir.write_file("file1", "foo\n");
364 work_dir.write_file("file2", "bar\n");
365
366 work_dir.run_jj(["commit", "-m=first", "file1"]).success();
367 let output = work_dir.run_jj(["diff", "-r", "@-"]);
368 insta::assert_snapshot!(output, @r"
369 Added regular file file1:
370 1: foo
371 [EOF]
372 ");
373
374 let output = work_dir.run_jj(["diff"]);
375 insta::assert_snapshot!(output, @r"
376 Added regular file file2:
377 1: bar
378 [EOF]
379 ");
380}
381
382#[test]
383fn test_commit_paths_warning() {
384 let test_env = TestEnvironment::default();
385 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
386 let work_dir = test_env.work_dir("repo");
387
388 work_dir.write_file("file1", "foo\n");
389 work_dir.write_file("file2", "bar\n");
390
391 let output = work_dir.run_jj(["commit", "-m=first", "file3"]);
392 insta::assert_snapshot!(output, @r"
393 ------- stderr -------
394 Warning: The given paths do not match any file: file3
395 Working copy (@) now at: rlvkpnrz d1872100 (no description set)
396 Parent commit (@-) : qpvuntsm fa15625b (empty) first
397 [EOF]
398 ");
399
400 let output = work_dir.run_jj(["diff"]);
401 insta::assert_snapshot!(output, @r"
402 Added regular file file1:
403 1: foo
404 Added regular file file2:
405 1: bar
406 [EOF]
407 ");
408}
409
410#[test]
411fn test_commit_reset_author() {
412 let test_env = TestEnvironment::default();
413 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
414 let work_dir = test_env.work_dir("repo");
415
416 test_env.add_config(
417 r#"[template-aliases]
418'format_signature(signature)' = 'signature.name() ++ " " ++ signature.email() ++ " " ++ signature.timestamp()'"#,
419 );
420 let get_signatures = || {
421 let template = r#"format_signature(author) ++ "\n" ++ format_signature(committer)"#;
422 work_dir.run_jj(["log", "-r@", "-T", template])
423 };
424 insta::assert_snapshot!(get_signatures(), @r"
425 @ Test User test.user@example.com 2001-02-03 04:05:07.000 +07:00
426 │ Test User test.user@example.com 2001-02-03 04:05:07.000 +07:00
427 ~
428 [EOF]
429 ");
430
431 // Reset the author (the committer is always reset)
432 work_dir
433 .run_jj([
434 "commit",
435 "--config=user.name=Ove Ridder",
436 "--config=user.email=ove.ridder@example.com",
437 "--reset-author",
438 "-m1",
439 ])
440 .success();
441 insta::assert_snapshot!(get_signatures(), @r"
442 @ Ove Ridder ove.ridder@example.com 2001-02-03 04:05:09.000 +07:00
443 │ Ove Ridder ove.ridder@example.com 2001-02-03 04:05:09.000 +07:00
444 ~
445 [EOF]
446 ");
447}
448
449#[must_use]
450fn get_log_output(work_dir: &TestWorkDir) -> CommandOutput {
451 let template = r#"commit_id.short() ++ " " ++ description"#;
452 work_dir.run_jj(["log", "-T", template])
453}