just playing with tangled
1// Copyright 2023 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 regex::Regex;
16
17use crate::common::TestEnvironment;
18
19#[test]
20fn test_log_parents() {
21 let test_env = TestEnvironment::default();
22 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
23 let repo_path = test_env.env_root().join("repo");
24
25 test_env.jj_cmd_ok(&repo_path, &["new"]);
26 test_env.jj_cmd_ok(&repo_path, &["new", "@-"]);
27 test_env.jj_cmd_ok(&repo_path, &["new", "@", "@-"]);
28
29 let template =
30 r#"commit_id ++ "\nP: " ++ parents.len() ++ " " ++ parents.map(|c| c.commit_id()) ++ "\n""#;
31 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]);
32 insta::assert_snapshot!(stdout, @r###"
33 @ c067170d4ca1bc6162b64f7550617ec809647f84
34 ├─╮ P: 2 4db490c88528133d579540b6900b8098f0c17701 230dd059e1b059aefc0da06a2e5a7dbf22362f22
35 ◉ │ 4db490c88528133d579540b6900b8098f0c17701
36 ├─╯ P: 1 230dd059e1b059aefc0da06a2e5a7dbf22362f22
37 ◉ 230dd059e1b059aefc0da06a2e5a7dbf22362f22
38 │ P: 1 0000000000000000000000000000000000000000
39 ◉ 0000000000000000000000000000000000000000
40 P: 0
41 "###);
42
43 let template = r#"parents.map(|c| c.commit_id().shortest(4))"#;
44 let stdout = test_env.jj_cmd_success(
45 &repo_path,
46 &["log", "-T", template, "-r@", "--color=always"],
47 );
48 insta::assert_snapshot!(stdout, @r###"
49 @ [1m[38;5;4m4[0m[38;5;8mdb4[39m [1m[38;5;4m2[0m[38;5;8m30d[39m
50 │
51 ~
52 "###);
53
54 // Commit object isn't printable
55 let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-T", "parents"]);
56 insta::assert_snapshot!(stderr, @r###"
57 Error: Failed to parse template: Expected expression of type "Template", but actual type is "List<Commit>"
58 Caused by: --> 1:1
59 |
60 1 | parents
61 | ^-----^
62 |
63 = Expected expression of type "Template", but actual type is "List<Commit>"
64 "###);
65
66 // Redundant argument passed to keyword method
67 let template = r#"parents.map(|c| c.commit_id(""))"#;
68 let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-T", template]);
69 insta::assert_snapshot!(stderr, @r###"
70 Error: Failed to parse template: Function "commit_id": Expected 0 arguments
71 Caused by: --> 1:29
72 |
73 1 | parents.map(|c| c.commit_id(""))
74 | ^^
75 |
76 = Function "commit_id": Expected 0 arguments
77 "###);
78}
79
80#[test]
81fn test_log_author_timestamp() {
82 let test_env = TestEnvironment::default();
83 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
84 let repo_path = test_env.env_root().join("repo");
85
86 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "first"]);
87 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "second"]);
88
89 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "author.timestamp()"]);
90 insta::assert_snapshot!(stdout, @r###"
91 @ 2001-02-03 04:05:09.000 +07:00
92 ◉ 2001-02-03 04:05:07.000 +07:00
93 ◉ 1970-01-01 00:00:00.000 +00:00
94 "###);
95}
96
97#[test]
98fn test_log_author_timestamp_ago() {
99 let test_env = TestEnvironment::default();
100 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
101 let repo_path = test_env.env_root().join("repo");
102
103 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "first"]);
104 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "second"]);
105
106 let template = r#"author.timestamp().ago() ++ "\n""#;
107 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--no-graph", "-T", template]);
108 let line_re = Regex::new(r"[0-9]+ years ago").unwrap();
109 assert!(
110 stdout.lines().all(|x| line_re.is_match(x)),
111 "expected every line to match regex"
112 );
113}
114
115#[test]
116fn test_log_author_timestamp_utc() {
117 let test_env = TestEnvironment::default();
118 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
119 let repo_path = test_env.env_root().join("repo");
120
121 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "author.timestamp().utc()"]);
122 insta::assert_snapshot!(stdout, @r###"
123 @ 2001-02-02 21:05:07.000 +00:00
124 ◉ 1970-01-01 00:00:00.000 +00:00
125 "###);
126}
127
128#[cfg(unix)]
129#[test]
130fn test_log_author_timestamp_local() {
131 let mut test_env = TestEnvironment::default();
132 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
133 let repo_path = test_env.env_root().join("repo");
134
135 test_env.add_env_var("TZ", "UTC-05:30");
136 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "author.timestamp().local()"]);
137 insta::assert_snapshot!(stdout, @r###"
138 @ 2001-02-03 08:05:07.000 +11:00
139 ◉ 1970-01-01 11:00:00.000 +11:00
140 "###);
141 test_env.add_env_var("TZ", "UTC+10:00");
142 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "author.timestamp().local()"]);
143 insta::assert_snapshot!(stdout, @r###"
144 @ 2001-02-03 08:05:07.000 +11:00
145 ◉ 1970-01-01 11:00:00.000 +11:00
146 "###);
147}
148
149#[test]
150fn test_mine_is_true_when_author_is_user() {
151 let test_env = TestEnvironment::default();
152 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
153 let repo_path = test_env.env_root().join("repo");
154 test_env.jj_cmd_ok(
155 &repo_path,
156 &[
157 "--config-toml=user.email='johndoe@example.com'",
158 "--config-toml=user.name='John Doe'",
159 "new",
160 ],
161 );
162
163 let stdout = test_env.jj_cmd_success(
164 &repo_path,
165 &[
166 "log",
167 "-T",
168 r#"coalesce(if(mine, "mine"), author.email(), email_placeholder)"#,
169 ],
170 );
171 insta::assert_snapshot!(stdout, @r###"
172 @ johndoe@example.com
173 ◉ mine
174 ◉ (no email set)
175 "###);
176}
177
178#[test]
179fn test_log_default() {
180 let test_env = TestEnvironment::default();
181 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
182 let repo_path = test_env.env_root().join("repo");
183
184 std::fs::write(repo_path.join("file1"), "foo\n").unwrap();
185 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "add a file"]);
186 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "description 1"]);
187 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
188
189 // Test default log output format
190 let stdout = test_env.jj_cmd_success(&repo_path, &["log"]);
191 insta::assert_snapshot!(stdout, @r###"
192 @ kkmpptxz test.user@example.com 2001-02-03 08:05:09 my-branch 9de54178
193 │ (empty) description 1
194 ◉ qpvuntsm test.user@example.com 2001-02-03 08:05:08 4291e264
195 │ add a file
196 ◉ zzzzzzzz root() 00000000
197 "###);
198
199 // Color
200 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always"]);
201 insta::assert_snapshot!(stdout, @r###"
202 @ [1m[38;5;13mk[38;5;8mkmpptxz[39m [38;5;3mtest.user@example.com[39m [38;5;14m2001-02-03 08:05:09[39m [38;5;13mmy-branch[39m [38;5;12m9[38;5;8mde54178[39m[0m
203 │ [1m[38;5;10m(empty)[39m description 1[0m
204 ◉ [1m[38;5;5mq[0m[38;5;8mpvuntsm[39m [38;5;3mtest.user@example.com[39m [38;5;6m2001-02-03 08:05:08[39m [1m[38;5;4m4[0m[38;5;8m291e264[39m
205 │ add a file
206 ◉ [1m[38;5;5mz[0m[38;5;8mzzzzzzz[39m [38;5;2mroot()[39m [1m[38;5;4m0[0m[38;5;8m0000000[39m
207 "###);
208
209 // Color without graph
210 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always", "--no-graph"]);
211 insta::assert_snapshot!(stdout, @r###"
212 [1m[38;5;13mk[38;5;8mkmpptxz[39m [38;5;3mtest.user@example.com[39m [38;5;14m2001-02-03 08:05:09[39m [38;5;13mmy-branch[39m [38;5;12m9[38;5;8mde54178[39m[0m
213 [1m[38;5;10m(empty)[39m description 1[0m
214 [1m[38;5;5mq[0m[38;5;8mpvuntsm[39m [38;5;3mtest.user@example.com[39m [38;5;6m2001-02-03 08:05:08[39m [1m[38;5;4m4[0m[38;5;8m291e264[39m
215 add a file
216 [1m[38;5;5mz[0m[38;5;8mzzzzzzz[39m [38;5;2mroot()[39m [1m[38;5;4m0[0m[38;5;8m0000000[39m
217 "###);
218}
219
220#[test]
221fn test_log_builtin_templates() {
222 let test_env = TestEnvironment::default();
223 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
224 let repo_path = test_env.env_root().join("repo");
225 // Render without graph and append "[EOF]" marker to test line ending
226 let render = |template| {
227 test_env.jj_cmd_success(&repo_path, &["log", "-T", template, "--no-graph"]) + "[EOF]\n"
228 };
229
230 test_env.jj_cmd_ok(
231 &repo_path,
232 &[
233 "--config-toml=user.email=''",
234 "--config-toml=user.name=''",
235 "new",
236 ],
237 );
238 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
239
240 insta::assert_snapshot!(render(r#"builtin_log_oneline"#), @r###"
241 rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397 (empty) (no description set)
242 qpvuntsm test.user 2001-02-03 08:05:07 230dd059 (empty) (no description set)
243 zzzzzzzz root() 00000000
244 [EOF]
245 "###);
246
247 insta::assert_snapshot!(render(r#"builtin_log_compact"#), @r###"
248 rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397
249 (empty) (no description set)
250 qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059
251 (empty) (no description set)
252 zzzzzzzz root() 00000000
253 [EOF]
254 "###);
255
256 insta::assert_snapshot!(render(r#"builtin_log_comfortable"#), @r###"
257 rlvkpnrz (no email set) 2001-02-03 08:05:08 my-branch dc315397
258 (empty) (no description set)
259
260 qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059
261 (empty) (no description set)
262
263 zzzzzzzz root() 00000000
264
265 [EOF]
266 "###);
267
268 insta::assert_snapshot!(render(r#"builtin_log_detailed"#), @r###"
269 Commit ID: dc31539712c7294d1d712cec63cef4504b94ca74
270 Change ID: rlvkpnrzqnoowoytxnquwvuryrwnrmlp
271 Branches: my-branch
272 Author: (no name set) <(no email set)> (2001-02-03 08:05:08)
273 Committer: (no name set) <(no email set)> (2001-02-03 08:05:08)
274
275 (no description set)
276
277 Commit ID: 230dd059e1b059aefc0da06a2e5a7dbf22362f22
278 Change ID: qpvuntsmwlqtpsluzzsnyyzlmlwvmlnu
279 Author: Test User <test.user@example.com> (2001-02-03 08:05:07)
280 Committer: Test User <test.user@example.com> (2001-02-03 08:05:07)
281
282 (no description set)
283
284 Commit ID: 0000000000000000000000000000000000000000
285 Change ID: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
286 Author: (no name set) <(no email set)> (1970-01-01 11:00:00)
287 Committer: (no name set) <(no email set)> (1970-01-01 11:00:00)
288
289 (no description set)
290
291 [EOF]
292 "###);
293}
294
295#[test]
296fn test_log_builtin_templates_colored() {
297 let test_env = TestEnvironment::default();
298 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
299 let repo_path = test_env.env_root().join("repo");
300 let render =
301 |template| test_env.jj_cmd_success(&repo_path, &["--color=always", "log", "-T", template]);
302
303 test_env.jj_cmd_ok(
304 &repo_path,
305 &[
306 "--config-toml=user.email=''",
307 "--config-toml=user.name=''",
308 "new",
309 ],
310 );
311 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "my-branch"]);
312
313 insta::assert_snapshot!(render(r#"builtin_log_oneline"#), @r###"
314 @ [1m[38;5;13mr[38;5;8mlvkpnrz[39m [38;5;9m(no email set)[39m [38;5;14m2001-02-03 08:05:08[39m [38;5;13mmy-branch[39m [38;5;12md[38;5;8mc315397[39m [38;5;10m(empty)[39m [38;5;10m(no description set)[39m[0m
315 ◉ [1m[38;5;5mq[0m[38;5;8mpvuntsm[39m [38;5;3mtest.user[39m [38;5;6m2001-02-03 08:05:07[39m [1m[38;5;4m2[0m[38;5;8m30dd059[39m [38;5;2m(empty)[39m [38;5;2m(no description set)[39m
316 ◉ [1m[38;5;5mz[0m[38;5;8mzzzzzzz[39m [38;5;2mroot()[39m [1m[38;5;4m0[0m[38;5;8m0000000[39m
317 "###);
318
319 insta::assert_snapshot!(render(r#"builtin_log_compact"#), @r###"
320 @ [1m[38;5;13mr[38;5;8mlvkpnrz[39m [38;5;9m(no email set)[39m [38;5;14m2001-02-03 08:05:08[39m [38;5;13mmy-branch[39m [38;5;12md[38;5;8mc315397[39m[0m
321 │ [1m[38;5;10m(empty)[39m [38;5;10m(no description set)[39m[0m
322 ◉ [1m[38;5;5mq[0m[38;5;8mpvuntsm[39m [38;5;3mtest.user@example.com[39m [38;5;6m2001-02-03 08:05:07[39m [1m[38;5;4m2[0m[38;5;8m30dd059[39m
323 │ [38;5;2m(empty)[39m [38;5;2m(no description set)[39m
324 ◉ [1m[38;5;5mz[0m[38;5;8mzzzzzzz[39m [38;5;2mroot()[39m [1m[38;5;4m0[0m[38;5;8m0000000[39m
325 "###);
326
327 insta::assert_snapshot!(render(r#"builtin_log_comfortable"#), @r###"
328 @ [1m[38;5;13mr[38;5;8mlvkpnrz[39m [38;5;9m(no email set)[39m [38;5;14m2001-02-03 08:05:08[39m [38;5;13mmy-branch[39m [38;5;12md[38;5;8mc315397[39m[0m
329 │ [1m[38;5;10m(empty)[39m [38;5;10m(no description set)[39m[0m
330 │
331 ◉ [1m[38;5;5mq[0m[38;5;8mpvuntsm[39m [38;5;3mtest.user@example.com[39m [38;5;6m2001-02-03 08:05:07[39m [1m[38;5;4m2[0m[38;5;8m30dd059[39m
332 │ [38;5;2m(empty)[39m [38;5;2m(no description set)[39m
333 │
334 ◉ [1m[38;5;5mz[0m[38;5;8mzzzzzzz[39m [38;5;2mroot()[39m [1m[38;5;4m0[0m[38;5;8m0000000[39m
335 "###);
336
337 insta::assert_snapshot!(render(r#"builtin_log_detailed"#), @r###"
338 @ Commit ID: [38;5;4mdc31539712c7294d1d712cec63cef4504b94ca74[39m
339 │ Change ID: [38;5;5mrlvkpnrzqnoowoytxnquwvuryrwnrmlp[39m
340 │ Branches: [38;5;5mmy-branch[39m
341 │ Author: [38;5;1m(no name set)[39m <[38;5;1m(no email set)[39m> ([38;5;6m2001-02-03 08:05:08[39m)
342 │ Committer: [38;5;1m(no name set)[39m <[38;5;1m(no email set)[39m> ([38;5;6m2001-02-03 08:05:08[39m)
343 │
344 │ [38;5;2m (no description set)[39m
345 │
346 ◉ Commit ID: [38;5;4m230dd059e1b059aefc0da06a2e5a7dbf22362f22[39m
347 │ Change ID: [38;5;5mqpvuntsmwlqtpsluzzsnyyzlmlwvmlnu[39m
348 │ Author: Test User <[38;5;3mtest.user@example.com[39m> ([38;5;6m2001-02-03 08:05:07[39m)
349 │ Committer: Test User <[38;5;3mtest.user@example.com[39m> ([38;5;6m2001-02-03 08:05:07[39m)
350 │
351 │ [38;5;2m (no description set)[39m
352 │
353 ◉ Commit ID: [38;5;4m0000000000000000000000000000000000000000[39m
354 Change ID: [38;5;5mzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz[39m
355 Author: [38;5;1m(no name set)[39m <[38;5;1m(no email set)[39m> ([38;5;6m1970-01-01 11:00:00[39m)
356 Committer: [38;5;1m(no name set)[39m <[38;5;1m(no email set)[39m> ([38;5;6m1970-01-01 11:00:00[39m)
357
358 [38;5;2m (no description set)[39m
359
360 "###);
361}
362
363#[test]
364fn test_log_obslog_divergence() {
365 let test_env = TestEnvironment::default();
366 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
367 let repo_path = test_env.env_root().join("repo");
368
369 std::fs::write(repo_path.join("file"), "foo\n").unwrap();
370 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "description 1"]);
371 let stdout = test_env.jj_cmd_success(&repo_path, &["log"]);
372 // No divergence
373 insta::assert_snapshot!(stdout, @r###"
374 @ qpvuntsm test.user@example.com 2001-02-03 08:05:08 7a17d52e
375 │ description 1
376 ◉ zzzzzzzz root() 00000000
377 "###);
378
379 // Create divergence
380 test_env.jj_cmd_ok(
381 &repo_path,
382 &["describe", "-m", "description 2", "--at-operation", "@-"],
383 );
384 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log"]);
385 insta::assert_snapshot!(stdout, @r###"
386 ◉ qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 8979953d
387 │ description 2
388 │ @ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 7a17d52e
389 ├─╯ description 1
390 ◉ zzzzzzzz root() 00000000
391 "###);
392 insta::assert_snapshot!(stderr, @r###"
393 Concurrent modification detected, resolving automatically.
394 "###);
395
396 // Color
397 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always"]);
398 insta::assert_snapshot!(stdout, @r###"
399 ◉ [1m[4m[38;5;1mq[0m[38;5;1mpvuntsm??[39m [38;5;3mtest.user@example.com[39m [38;5;6m2001-02-03 08:05:10[39m [1m[38;5;4m8[0m[38;5;8m979953d[39m
400 │ description 2
401 │ @ [1m[4m[38;5;1mq[24mpvuntsm[38;5;9m??[39m [38;5;3mtest.user@example.com[39m [38;5;14m2001-02-03 08:05:08[39m [38;5;12m7[38;5;8ma17d52e[39m[0m
402 ├─╯ [1mdescription 1[0m
403 ◉ [1m[38;5;5mz[0m[38;5;8mzzzzzzz[39m [38;5;2mroot()[39m [1m[38;5;4m0[0m[38;5;8m0000000[39m
404 "###);
405
406 // Obslog and hidden divergent
407 let stdout = test_env.jj_cmd_success(&repo_path, &["obslog"]);
408 insta::assert_snapshot!(stdout, @r###"
409 @ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 7a17d52e
410 │ description 1
411 ◉ qpvuntsm hidden test.user@example.com 2001-02-03 08:05:08 3b68ce25
412 │ (no description set)
413 ◉ qpvuntsm hidden test.user@example.com 2001-02-03 08:05:07 230dd059
414 (empty) (no description set)
415 "###);
416
417 // Colored obslog
418 let stdout = test_env.jj_cmd_success(&repo_path, &["obslog", "--color=always"]);
419 insta::assert_snapshot!(stdout, @r###"
420 @ [1m[4m[38;5;1mq[24mpvuntsm[38;5;9m??[39m [38;5;3mtest.user@example.com[39m [38;5;14m2001-02-03 08:05:08[39m [38;5;12m7[38;5;8ma17d52e[39m[0m
421 │ [1mdescription 1[0m
422 ◉ [1m[39mq[0m[38;5;8mpvuntsm[39m hidden [38;5;3mtest.user@example.com[39m [38;5;6m2001-02-03 08:05:08[39m [1m[38;5;4m3[0m[38;5;8mb68ce25[39m
423 │ [38;5;3m(no description set)[39m
424 ◉ [1m[39mq[0m[38;5;8mpvuntsm[39m hidden [38;5;3mtest.user@example.com[39m [38;5;6m2001-02-03 08:05:07[39m [1m[38;5;4m2[0m[38;5;8m30dd059[39m
425 [38;5;2m(empty)[39m [38;5;2m(no description set)[39m
426 "###);
427}
428
429#[test]
430fn test_log_branches() {
431 let test_env = TestEnvironment::default();
432 test_env.add_config("git.auto-local-branch = true");
433 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
434
435 test_env.jj_cmd_ok(test_env.env_root(), &["init", "--git", "origin"]);
436 let origin_path = test_env.env_root().join("origin");
437 let origin_git_repo_path = origin_path
438 .join(".jj")
439 .join("repo")
440 .join("store")
441 .join("git");
442
443 // Created some branches on the remote
444 test_env.jj_cmd_ok(&origin_path, &["describe", "-m=description 1"]);
445 test_env.jj_cmd_ok(&origin_path, &["branch", "create", "branch1"]);
446 test_env.jj_cmd_ok(&origin_path, &["new", "root()", "-m=description 2"]);
447 test_env.jj_cmd_ok(&origin_path, &["branch", "create", "branch2", "unchanged"]);
448 test_env.jj_cmd_ok(&origin_path, &["new", "root()", "-m=description 3"]);
449 test_env.jj_cmd_ok(&origin_path, &["branch", "create", "branch3"]);
450 test_env.jj_cmd_ok(&origin_path, &["git", "export"]);
451 test_env.jj_cmd_ok(
452 test_env.env_root(),
453 &[
454 "git",
455 "clone",
456 origin_git_repo_path.to_str().unwrap(),
457 "local",
458 ],
459 );
460 let workspace_root = test_env.env_root().join("local");
461
462 // Rewrite branch1, move branch2 forward, create conflict in branch3, add
463 // new-branch
464 test_env.jj_cmd_ok(
465 &workspace_root,
466 &["describe", "branch1", "-m", "modified branch1 commit"],
467 );
468 test_env.jj_cmd_ok(&workspace_root, &["new", "branch2"]);
469 test_env.jj_cmd_ok(&workspace_root, &["branch", "set", "branch2"]);
470 test_env.jj_cmd_ok(&workspace_root, &["branch", "create", "new-branch"]);
471 test_env.jj_cmd_ok(&workspace_root, &["describe", "branch3", "-m=local"]);
472 test_env.jj_cmd_ok(&origin_path, &["describe", "branch3", "-m=origin"]);
473 test_env.jj_cmd_ok(&origin_path, &["git", "export"]);
474 test_env.jj_cmd_ok(&workspace_root, &["git", "fetch"]);
475
476 let template = r#"commit_id.short() ++ " " ++ if(branches, branches, "(no branches)")"#;
477 let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
478 insta::assert_snapshot!(output, @r###"
479 ◉ fed794e2ba44 branch3?? branch3@origin
480 │ ◉ b1bb3766d584 branch3??
481 ├─╯
482 │ ◉ 21c33875443e branch1*
483 ├─╯
484 │ @ a5b4d15489cc branch2* new-branch
485 │ ◉ 8476341eb395 branch2@origin unchanged
486 ├─╯
487 ◉ 000000000000 (no branches)
488 "###);
489
490 let template = r#"branches.map(|b| separate("/", b.remote(), b.name())).join(", ")"#;
491 let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
492 insta::assert_snapshot!(output, @r###"
493 ◉ branch3, origin/branch3
494 │ ◉ branch3
495 ├─╯
496 │ ◉ branch1
497 ├─╯
498 │ @ branch2, new-branch
499 │ ◉ origin/branch2, unchanged
500 ├─╯
501 ◉
502 "###);
503
504 let template = r#"separate(" ", "L:", local_branches, "R:", remote_branches)"#;
505 let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
506 insta::assert_snapshot!(output, @r###"
507 ◉ L: branch3?? R: branch3@origin
508 │ ◉ L: branch3?? R:
509 ├─╯
510 │ ◉ L: branch1* R:
511 ├─╯
512 │ @ L: branch2* new-branch R:
513 │ ◉ L: unchanged R: branch2@origin unchanged@origin
514 ├─╯
515 ◉ L: R:
516 "###);
517}
518
519#[test]
520fn test_log_git_head() {
521 let test_env = TestEnvironment::default();
522 let repo_path = test_env.env_root().join("repo");
523 git2::Repository::init(&repo_path).unwrap();
524 test_env.jj_cmd_ok(&repo_path, &["init", "--git-repo=."]);
525
526 test_env.jj_cmd_ok(&repo_path, &["new", "-m=initial"]);
527 std::fs::write(repo_path.join("file"), "foo\n").unwrap();
528
529 let template = r#"
530 separate(", ",
531 if(git_head, "name: " ++ git_head.name()),
532 "remote: " ++ git_head.remote(),
533 ) ++ "\n"
534 "#;
535 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]);
536 insta::assert_snapshot!(stdout, @r###"
537 @ remote: <Error: No RefName available>
538 ◉ name: HEAD, remote: git
539 ◉ remote: <Error: No RefName available>
540 "###);
541
542 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always"]);
543 insta::assert_snapshot!(stdout, @r###"
544 @ [1m[38;5;13mr[38;5;8mlvkpnrz[39m [38;5;3mtest.user@example.com[39m [38;5;14m2001-02-03 08:05:09[39m [38;5;12m5[38;5;8m0aaf475[39m[0m
545 │ [1minitial[0m
546 ◉ [1m[38;5;5mq[0m[38;5;8mpvuntsm[39m [38;5;3mtest.user@example.com[39m [38;5;6m2001-02-03 08:05:07[39m [38;5;2mHEAD@git[39m [1m[38;5;4m2[0m[38;5;8m30dd059[39m
547 │ [38;5;2m(empty)[39m [38;5;2m(no description set)[39m
548 ◉ [1m[38;5;5mz[0m[38;5;8mzzzzzzz[39m [38;5;2mroot()[39m [1m[38;5;4m0[0m[38;5;8m0000000[39m
549 "###);
550}
551
552#[test]
553fn test_log_customize_short_id() {
554 let test_env = TestEnvironment::default();
555 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
556 let repo_path = test_env.env_root().join("repo");
557
558 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "first"]);
559
560 // Customize both the commit and the change id
561 let decl = "template-aliases.'format_short_id(id)'";
562 let stdout = test_env.jj_cmd_success(
563 &repo_path,
564 &[
565 "log",
566 "--config-toml",
567 &format!(r#"{decl}='id.shortest(5).prefix().upper() ++ "_" ++ id.shortest(5).rest()'"#),
568 ],
569 );
570 insta::assert_snapshot!(stdout, @r###"
571 @ Q_pvun test.user@example.com 2001-02-03 08:05:08 6_9542
572 │ (empty) first
573 ◉ Z_zzzz root() 0_0000
574 "###);
575
576 // Customize only the change id
577 let stdout = test_env.jj_cmd_success(
578 &repo_path,
579 &[
580 "log",
581 "--config-toml",
582 r#"
583 [template-aliases]
584 'format_short_change_id(id)'='format_short_id(id).upper()'
585 "#,
586 ],
587 );
588 insta::assert_snapshot!(stdout, @r###"
589 @ QPVUNTSM test.user@example.com 2001-02-03 08:05:08 69542c19
590 │ (empty) first
591 ◉ ZZZZZZZZ root() 00000000
592 "###);
593}
594
595#[test]
596fn test_log_immutable() {
597 let test_env = TestEnvironment::default();
598 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
599 let repo_path = test_env.env_root().join("repo");
600 test_env.jj_cmd_ok(&repo_path, &["new", "-mA", "root()"]);
601 test_env.jj_cmd_ok(&repo_path, &["new", "-mB"]);
602 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
603 test_env.jj_cmd_ok(&repo_path, &["new", "-mC"]);
604 test_env.jj_cmd_ok(&repo_path, &["new", "-mD", "root()"]);
605
606 let template = r#"
607 separate(" ",
608 description.first_line(),
609 branches,
610 if(immutable, "[immutable]"),
611 ) ++ "\n"
612 "#;
613
614 test_env.add_config("revset-aliases.'immutable_heads()' = 'main'");
615 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r::", "-T", template]);
616 insta::assert_snapshot!(stdout, @r###"
617 @ D
618 │ ◉ C
619 │ ◉ B main [immutable]
620 │ ◉ A [immutable]
621 ├─╯
622 ◉ [immutable]
623 "###);
624
625 // Suppress error that could be detected earlier
626 test_env.add_config("revsets.short-prefixes = ''");
627
628 test_env.add_config("revset-aliases.'immutable_heads()' = 'unknown_fn()'");
629 let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r::", "-T", template]);
630 insta::assert_snapshot!(stderr, @r###"
631 Error: Failed to parse template: Failed to parse revset
632 Caused by:
633 1: --> 5:10
634 |
635 5 | if(immutable, "[immutable]"),
636 | ^-------^
637 |
638 = Failed to parse revset
639 2: --> 1:1
640 |
641 1 | unknown_fn()
642 | ^--------^
643 |
644 = Function "unknown_fn" doesn't exist
645 "###);
646
647 test_env.add_config("revset-aliases.'immutable_heads()' = 'unknown_symbol'");
648 let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r::", "-T", template]);
649 insta::assert_snapshot!(stderr, @r###"
650 Error: Failed to parse template: Failed to evaluate revset
651 Caused by:
652 1: --> 5:10
653 |
654 5 | if(immutable, "[immutable]"),
655 | ^-------^
656 |
657 = Failed to evaluate revset
658 2: Revision "unknown_symbol" doesn't exist
659 "###);
660}
661
662#[test]
663fn test_log_contained_in() {
664 let test_env = TestEnvironment::default();
665 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
666 let repo_path = test_env.env_root().join("repo");
667 test_env.jj_cmd_ok(&repo_path, &["new", "-mA", "root()"]);
668 test_env.jj_cmd_ok(&repo_path, &["new", "-mB"]);
669 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]);
670 test_env.jj_cmd_ok(&repo_path, &["new", "-mC"]);
671 test_env.jj_cmd_ok(&repo_path, &["new", "-mD", "root()"]);
672
673 let template_for_revset = |revset: &str| {
674 format!(
675 r#"
676 separate(" ",
677 description.first_line(),
678 branches,
679 if(self.contained_in("{revset}"), "[contained_in]"),
680 ) ++ "\n"
681 "#
682 )
683 };
684
685 let stdout = test_env.jj_cmd_success(
686 &repo_path,
687 &[
688 "log",
689 "-r::",
690 "-T",
691 &template_for_revset(r#"description(A)::"#),
692 ],
693 );
694 insta::assert_snapshot!(stdout, @r###"
695 @ D
696 │ ◉ C [contained_in]
697 │ ◉ B main [contained_in]
698 │ ◉ A [contained_in]
699 ├─╯
700 ◉
701 "###);
702
703 let stdout = test_env.jj_cmd_success(
704 &repo_path,
705 &[
706 "log",
707 "-r::",
708 "-T",
709 &template_for_revset(r#"visible_heads()"#),
710 ],
711 );
712 insta::assert_snapshot!(stdout, @r###"
713 @ D [contained_in]
714 │ ◉ C [contained_in]
715 │ ◉ B main
716 │ ◉ A
717 ├─╯
718 ◉
719 "###);
720
721 // Suppress error that could be detected earlier
722 let stderr = test_env.jj_cmd_failure(
723 &repo_path,
724 &["log", "-r::", "-T", &template_for_revset("unknown_fn()")],
725 );
726 insta::assert_snapshot!(stderr, @r###"
727 Error: Failed to parse template: Failed to parse revset
728 Caused by:
729 1: --> 5:28
730 |
731 5 | if(self.contained_in("unknown_fn()"), "[contained_in]"),
732 | ^------------^
733 |
734 = Failed to parse revset
735 2: --> 1:1
736 |
737 1 | unknown_fn()
738 | ^--------^
739 |
740 = Function "unknown_fn" doesn't exist
741 "###);
742
743 let stderr = test_env.jj_cmd_failure(
744 &repo_path,
745 &["log", "-r::", "-T", &template_for_revset("author(x:'y')")],
746 );
747 insta::assert_snapshot!(stderr, @r###"
748 Error: Failed to parse template: Failed to parse revset
749 Caused by:
750 1: --> 5:28
751 |
752 5 | if(self.contained_in("author(x:'y')"), "[contained_in]"),
753 | ^-------------^
754 |
755 = Failed to parse revset
756 2: --> 1:8
757 |
758 1 | author(x:'y')
759 | ^---^
760 |
761 = Function "author": Invalid string pattern
762 3: Invalid string pattern kind "x:"
763 Hint: Try prefixing with one of `exact:`, `glob:` or `substring:`
764 "###);
765
766 let stderr = test_env.jj_cmd_failure(
767 &repo_path,
768 &["log", "-r::", "-T", &template_for_revset("maine")],
769 );
770 insta::assert_snapshot!(stderr, @r###"
771 Error: Failed to parse template: Failed to evaluate revset
772 Caused by:
773 1: --> 5:28
774 |
775 5 | if(self.contained_in("maine"), "[contained_in]"),
776 | ^-----^
777 |
778 = Failed to evaluate revset
779 2: Revision "maine" doesn't exist
780 Hint: Did you mean "main"?
781 "###);
782}