just playing with tangled
1// Copyright 2024 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::OpenOptions;
16use std::io::Write as _;
17use std::path::Path;
18
19use crate::common::TestEnvironment;
20
21fn append_to_file(file_path: &Path, contents: &str) {
22 let mut options = OpenOptions::new();
23 options.append(true);
24 let mut file = options.open(file_path).unwrap();
25 writeln!(file, "{contents}").unwrap();
26}
27
28#[test]
29fn test_annotate_linear() {
30 let test_env = TestEnvironment::default();
31 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
32 let work_dir = test_env.work_dir("repo");
33
34 work_dir.write_file("file.txt", "line1\n");
35 work_dir
36 .run_jj(["describe", "-m=initial", "--author=Foo <foo@example.org>"])
37 .success();
38
39 work_dir.run_jj(["new", "-m=next"]).success();
40 append_to_file(
41 &work_dir.root().join("file.txt"),
42 "new text from new commit",
43 );
44
45 let output = work_dir.run_jj(["file", "annotate", "file.txt"]);
46 insta::assert_snapshot!(output, @r"
47 qpvuntsm foo 2001-02-03 08:05:08 1: line1
48 kkmpptxz test.use 2001-02-03 08:05:10 2: new text from new commit
49 [EOF]
50 ");
51}
52
53#[test]
54fn test_annotate_merge() {
55 let test_env = TestEnvironment::default();
56 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
57 let work_dir = test_env.work_dir("repo");
58
59 work_dir.write_file("file.txt", "line1\n");
60 work_dir.run_jj(["describe", "-m=initial"]).success();
61 work_dir
62 .run_jj(["branch", "create", "-r@", "initial"])
63 .success();
64
65 work_dir.run_jj(["new", "-m=commit1"]).success();
66 append_to_file(
67 &work_dir.root().join("file.txt"),
68 "new text from new commit 1",
69 );
70 work_dir
71 .run_jj(["branch", "create", "-r@", "commit1"])
72 .success();
73
74 work_dir.run_jj(["new", "-m=commit2", "initial"]).success();
75 append_to_file(
76 &work_dir.root().join("file.txt"),
77 "new text from new commit 2",
78 );
79 work_dir
80 .run_jj(["branch", "create", "-r@", "commit2"])
81 .success();
82
83 // create a (conflicted) merge
84 work_dir
85 .run_jj(["new", "-m=merged", "commit1", "commit2"])
86 .success();
87 // resolve conflicts
88 work_dir.write_file(
89 "file.txt",
90 "line1\nnew text from new commit 1\nnew text from new commit 2\n",
91 );
92
93 let output = work_dir.run_jj(["file", "annotate", "file.txt"]);
94 insta::assert_snapshot!(output, @r"
95 qpvuntsm test.use 2001-02-03 08:05:08 1: line1
96 zsuskuln test.use 2001-02-03 08:05:11 2: new text from new commit 1
97 royxmykx test.use 2001-02-03 08:05:13 3: new text from new commit 2
98 [EOF]
99 ");
100}
101
102#[test]
103fn test_annotate_conflicted() {
104 let test_env = TestEnvironment::default();
105 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
106 let work_dir = test_env.work_dir("repo");
107
108 work_dir.write_file("file.txt", "line1\n");
109 work_dir.run_jj(["describe", "-m=initial"]).success();
110 work_dir
111 .run_jj(["branch", "create", "-r@", "initial"])
112 .success();
113
114 work_dir.run_jj(["new", "-m=commit1"]).success();
115 append_to_file(
116 &work_dir.root().join("file.txt"),
117 "new text from new commit 1",
118 );
119 work_dir
120 .run_jj(["branch", "create", "-r@", "commit1"])
121 .success();
122
123 work_dir.run_jj(["new", "-m=commit2", "initial"]).success();
124 append_to_file(
125 &work_dir.root().join("file.txt"),
126 "new text from new commit 2",
127 );
128 work_dir
129 .run_jj(["branch", "create", "-r@", "commit2"])
130 .success();
131
132 // create a (conflicted) merge
133 work_dir
134 .run_jj(["new", "-m=merged", "commit1", "commit2"])
135 .success();
136 work_dir.run_jj(["new"]).success();
137
138 let output = work_dir.run_jj(["file", "annotate", "file.txt"]);
139 insta::assert_snapshot!(output, @r"
140 qpvuntsm test.use 2001-02-03 08:05:08 1: line1
141 yostqsxw test.use 2001-02-03 08:05:15 2: <<<<<<< Conflict 1 of 1
142 yostqsxw test.use 2001-02-03 08:05:15 3: %%%%%%% Changes from base to side #1
143 yostqsxw test.use 2001-02-03 08:05:15 4: +new text from new commit 1
144 yostqsxw test.use 2001-02-03 08:05:15 5: +++++++ Contents of side #2
145 royxmykx test.use 2001-02-03 08:05:13 6: new text from new commit 2
146 yostqsxw test.use 2001-02-03 08:05:15 7: >>>>>>> Conflict 1 of 1 ends
147 [EOF]
148 ");
149}
150
151#[test]
152fn test_annotate_merge_one_sided_conflict_resolution() {
153 let test_env = TestEnvironment::default();
154 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
155 let work_dir = test_env.work_dir("repo");
156
157 work_dir.write_file("file.txt", "line1\n");
158 work_dir.run_jj(["describe", "-m=initial"]).success();
159 work_dir
160 .run_jj(["branch", "create", "-r@", "initial"])
161 .success();
162
163 work_dir.run_jj(["new", "-m=commit1"]).success();
164 append_to_file(
165 &work_dir.root().join("file.txt"),
166 "new text from new commit 1",
167 );
168 work_dir
169 .run_jj(["branch", "create", "-r@", "commit1"])
170 .success();
171
172 work_dir.run_jj(["new", "-m=commit2", "initial"]).success();
173 append_to_file(
174 &work_dir.root().join("file.txt"),
175 "new text from new commit 2",
176 );
177 work_dir
178 .run_jj(["branch", "create", "-r@", "commit2"])
179 .success();
180
181 // create a (conflicted) merge
182 work_dir
183 .run_jj(["new", "-m=merged", "commit1", "commit2"])
184 .success();
185 // resolve conflicts
186 work_dir.write_file("file.txt", "line1\nnew text from new commit 1\n");
187
188 let output = work_dir.run_jj(["file", "annotate", "file.txt"]);
189 insta::assert_snapshot!(output, @r"
190 qpvuntsm test.use 2001-02-03 08:05:08 1: line1
191 zsuskuln test.use 2001-02-03 08:05:11 2: new text from new commit 1
192 [EOF]
193 ");
194}
195
196#[test]
197fn test_annotate_with_template() {
198 let test_env = TestEnvironment::default();
199 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
200 let work_dir = test_env.work_dir("repo");
201
202 work_dir.write_file("file.txt", "line1\n");
203 work_dir.run_jj(["commit", "-m=initial"]).success();
204
205 append_to_file(
206 &work_dir.root().join("file.txt"),
207 "new text from new commit 1\nthat splits into multiple lines",
208 );
209 work_dir.run_jj(["commit", "-m=commit1"]).success();
210
211 append_to_file(
212 &work_dir.root().join("file.txt"),
213 "new text from new commit 2\nalso continuing on a second line\nand a third!",
214 );
215 work_dir.run_jj(["describe", "-m=commit2"]).success();
216
217 let template = indoc::indoc! {r#"
218 if(first_line_in_hunk, "\n" ++ separate("\n",
219 commit.change_id().shortest(8)
220 ++ " "
221 ++ commit.description().first_line(),
222 commit_timestamp(commit).local().format('%Y-%m-%d %H:%M:%S')
223 ++ " "
224 ++ commit.author(),
225 ) ++ "\n") ++ pad_start(4, line_number) ++ ": " ++ content
226 "#};
227
228 let output = work_dir.run_jj(["file", "annotate", "file.txt", "-T", template]);
229 insta::assert_snapshot!(output, @r"
230 qpvuntsm initial
231 2001-02-03 08:05:08 Test User <test.user@example.com>
232 1: line1
233
234 rlvkpnrz commit1
235 2001-02-03 08:05:09 Test User <test.user@example.com>
236 2: new text from new commit 1
237 3: that splits into multiple lines
238
239 kkmpptxz commit2
240 2001-02-03 08:05:10 Test User <test.user@example.com>
241 4: new text from new commit 2
242 5: also continuing on a second line
243 6: and a third!
244 [EOF]
245 ");
246}