+7
-8
CHANGELOG.md
+7
-8
CHANGELOG.md
···
67
67
* Templates can now do arithmetic on integers with the `+`, `-`, `*`, `/`, and `%`
68
68
infix operators.
69
69
70
-
* `jj split` assigns the change id and the bookmarks of the source revision
71
-
to the revision with the non-selected changes.
72
-
You can opt out of this change by setting `split.legacy-bookmark-behavior = true`,
73
-
but this will likely be removed in a future release.
74
-
75
70
* Evolution history is now stored in the operation log. `jj evolog` can show
76
71
associated operations for commits created by new jj.
77
72
···
83
78
84
79
* `jj parallelize` can now parallelize groups of changes that _start_ with an
85
80
immutable change, but do not contain any other immutable changes.
81
+
82
+
* `jj` will no longer warn about deprecated paths on macOS if the configured
83
+
XDG directory is the deprecated one (~/Library/Application Support).
86
84
87
85
### Packaging changes
88
86
···
120
118
`template-aliases.default_commit_description`. Please also consider using
121
119
[`templates.draft_commit_description`](docs/config.md#default-description),
122
120
and/or [`templates.commit_trailers`](docs/config.md#commit-trailers).
121
+
122
+
* On macOS, config.toml files in `~/Library/Application Support/jj` are
123
+
deprecated; one should instead use `$XDG_CONFIG_HOME/jj`
124
+
(defaults to `~/.config/jj`)
123
125
124
126
### New features
125
127
···
324
326
325
327
* The 'how to resolve conflicts' hint that is shown when conflicts appear can
326
328
be hidden by setting `hints.resolving-conflicts = false`.
327
-
328
-
* `jj squash` now has a `--restore-descendants` option to preserve the snapshots
329
-
of the children of the modified commits.
330
329
331
330
* `jj op diff` and `jj op log --op-diff` now show changes to which commits
332
331
correspond to working copies.
+2
-6
cli/build.rs
+2
-6
cli/build.rs
···
61
61
"log",
62
62
"--no-graph",
63
63
"-r=@-",
64
-
"-T=commit_id ++ '-'",
64
+
"-T=commit_id",
65
65
])
66
66
.output()
67
67
{
68
68
if output.status.success() {
69
-
let mut parent_commits = String::from_utf8(output.stdout).unwrap();
70
-
// TODO(ilyagr): The `test_version` integration test shoult be fixed
71
-
// to succeed even if there is more than one parent commit.
72
-
parent_commits.truncate(parent_commits.trim_end_matches('-').len());
73
-
return Some(parent_commits);
69
+
return Some(String::from_utf8(output.stdout).unwrap());
74
70
}
75
71
}
76
72
+13
-3
cli/src/cli_util.rs
+13
-3
cli/src/cli_util.rs
···
3858
3858
"Did you update to a commit where the directory doesn't exist?",
3859
3859
)
3860
3860
})?;
3861
-
let mut config_env = ConfigEnv::from_environment();
3861
+
let mut config_env = ConfigEnv::from_environment(ui);
3862
3862
let mut last_config_migration_descriptions = Vec::new();
3863
3863
let mut migrate_config = |config: &mut StackedConfig| -> Result<(), CommandError> {
3864
3864
last_config_migration_descriptions =
···
3937
3937
ui.reset(&config)?;
3938
3938
3939
3939
// Print only the last migration messages to omit duplicates.
3940
-
for desc in &last_config_migration_descriptions {
3941
-
writeln!(ui.warning_default(), "Deprecated config: {desc}")?;
3940
+
for (source, desc) in &last_config_migration_descriptions {
3941
+
let source_str = match source {
3942
+
ConfigSource::Default => "default-provided",
3943
+
ConfigSource::EnvBase | ConfigSource::EnvOverrides => "environment-provided",
3944
+
ConfigSource::User => "user-level",
3945
+
ConfigSource::Repo => "repo-level",
3946
+
ConfigSource::CommandArg => "CLI-provided",
3947
+
};
3948
+
writeln!(
3949
+
ui.warning_default(),
3950
+
"Deprecated {source_str} config: {desc}"
3951
+
)?;
3942
3952
}
3943
3953
3944
3954
if args.global_args.repository.is_some() {
+1
-6
cli/src/commands/commit.rs
+1
-6
cli/src/commands/commit.rs
···
12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
-
use clap_complete::ArgValueCandidates;
16
15
use clap_complete::ArgValueCompleter;
17
16
use indoc::writedoc;
18
17
use jj_lib::backend::Signature;
···
38
37
#[arg(short, long)]
39
38
interactive: bool,
40
39
/// Specify diff editor to be used (implies --interactive)
41
-
#[arg(
42
-
long,
43
-
value_name = "NAME",
44
-
add = ArgValueCandidates::new(complete::diff_editors),
45
-
)]
40
+
#[arg(long, value_name = "NAME")]
46
41
tool: Option<String>,
47
42
/// The change description to use (don't open editor)
48
43
#[arg(long = "message", short, value_name = "MESSAGE")]
+1
-6
cli/src/commands/diffedit.rs
+1
-6
cli/src/commands/diffedit.rs
···
14
14
15
15
use std::io::Write as _;
16
16
17
-
use clap_complete::ArgValueCandidates;
18
17
use clap_complete::ArgValueCompleter;
19
18
use itertools::Itertools as _;
20
19
use jj_lib::matchers::EverythingMatcher;
···
78
77
)]
79
78
to: Option<RevisionArg>,
80
79
/// Specify diff editor to be used
81
-
#[arg(
82
-
long,
83
-
value_name = "NAME",
84
-
add = ArgValueCandidates::new(complete::diff_editors),
85
-
)]
80
+
#[arg(long, value_name = "NAME")]
86
81
tool: Option<String>,
87
82
/// Preserve the content (not the diff) when rebasing descendants
88
83
///
+9
-60
cli/src/commands/evolog.rs
+9
-60
cli/src/commands/evolog.rs
···
19
19
use clap_complete::ArgValueCompleter;
20
20
use itertools::Itertools as _;
21
21
use jj_lib::commit::Commit;
22
-
use jj_lib::copies::CopyRecords;
23
22
use jj_lib::evolution::walk_predecessors;
24
23
use jj_lib::graph::reverse_graph;
25
24
use jj_lib::graph::GraphEdge;
26
25
use jj_lib::matchers::EverythingMatcher;
27
-
use jj_lib::repo::Repo as _;
28
-
use jj_lib::rewrite::merge_commit_trees;
29
26
use tracing::instrument;
30
27
31
28
use super::log::get_node_template;
···
35
32
use crate::cli_util::RevisionArg;
36
33
use crate::command_error::CommandError;
37
34
use crate::complete;
38
-
use crate::diff_util::get_copy_records;
39
35
use crate::diff_util::DiffFormatArgs;
40
36
use crate::graphlog::get_graphlog;
41
37
use crate::graphlog::GraphStyle;
···
89
85
/// contaminated by unrelated changes.
90
86
#[arg(long, short = 'p')]
91
87
patch: bool,
92
-
/// Changes the behavior of `--patch`, `--git`, etc to show diffs from
93
-
/// rebases
94
-
///
95
-
/// Implies `--patch` if no other diff format is requested.
96
-
///
97
-
/// Normally, `jj evolog -p` shows a so-called "interdiff", temporarily
98
-
/// rebasing the versions of a revision to the same parents, in order to
99
-
/// omit differences in the file contents that are caused by rebases.
100
-
///
101
-
/// This option disables this behavior, and shows diffs between the contents
102
-
/// of the different versions without modification (as snapshots).
103
-
///
104
-
/// Sometimes, `--diff-snapshots` can show fewer differences to be shown.
105
-
/// For example, let's say the current revision is not empty and we perform
106
-
/// `jj squash --keep-empty -r @` to make it empty. Then, `jj evolog -p
107
-
/// --diff-snapshots` will not show any changes since the contents of the
108
-
/// files in the current revision did not change. However, `jj evolog -p`
109
-
/// will show a change, representing the fact that a non-empty revision
110
-
/// became empty.
111
-
#[arg(long)]
112
-
diff_snapshots: bool,
113
88
#[command(flatten)]
114
89
diff_format: DiffFormatArgs,
115
90
}
···
124
99
125
100
let start_commit = workspace_command.resolve_single_rev(ui, &args.revision)?;
126
101
127
-
let diff_renderer = workspace_command
128
-
.diff_renderer_for_log(&args.diff_format, args.patch || args.diff_snapshots)?;
102
+
let diff_renderer = workspace_command.diff_renderer_for_log(&args.diff_format, args.patch)?;
129
103
let graph_style = GraphStyle::from_settings(workspace_command.settings())?;
130
104
let with_content_format = LogContentFormat::new(ui, workspace_command.settings())?;
131
105
···
199
173
if let Some(renderer) = &diff_renderer {
200
174
let predecessors: Vec<_> = entry.predecessors().try_collect()?;
201
175
let mut formatter = ui.new_formatter(&mut buffer);
202
-
if args.diff_snapshots {
203
-
let mut copy_records = CopyRecords::default();
204
-
for p in &predecessors {
205
-
let records = get_copy_records(
206
-
workspace_command.repo().store(),
207
-
p.id(),
208
-
entry.commit.id(),
209
-
&EverythingMatcher,
210
-
)?;
211
-
copy_records.add_records(records)?;
212
-
}
213
-
let from_tree =
214
-
merge_commit_trees(workspace_command.repo().as_ref(), &predecessors)?;
215
-
renderer.show_diff(
216
-
ui,
217
-
formatter.as_mut(),
218
-
&from_tree,
219
-
&entry.commit.tree()?,
220
-
&EverythingMatcher,
221
-
©_records,
222
-
within_graph.width(),
223
-
)?;
224
-
} else {
225
-
renderer.show_inter_diff(
226
-
ui,
227
-
formatter.as_mut(),
228
-
&predecessors,
229
-
&entry.commit,
230
-
&EverythingMatcher,
231
-
within_graph.width(),
232
-
)?;
233
-
}
176
+
renderer.show_inter_diff(
177
+
ui,
178
+
formatter.as_mut(),
179
+
&predecessors,
180
+
&entry.commit,
181
+
&EverythingMatcher,
182
+
within_graph.width(),
183
+
)?;
234
184
}
235
185
let node_symbol = format_template(ui, &Some(entry.commit.clone()), &node_template);
236
186
graph.add_node(
···
262
212
if let Some(renderer) = &diff_renderer {
263
213
let predecessors: Vec<_> = entry.predecessors().try_collect()?;
264
214
let width = ui.term_width();
265
-
// TODO
266
215
renderer.show_inter_diff(
267
216
ui,
268
217
formatter,
+4
-10
cli/src/commands/git/fetch.rs
+4
-10
cli/src/commands/git/fetch.rs
···
189
189
branches: &[StringPattern],
190
190
remotes: &[&RemoteName],
191
191
) -> Result<(), CommandError> {
192
-
let mut missing_branches = vec![];
193
192
for branch in branches {
194
193
let matches = remotes.iter().any(|&remote| {
195
194
let remote = StringPattern::exact(remote);
···
206
205
.is_some()
207
206
});
208
207
if !matches {
209
-
missing_branches.push(branch);
208
+
writeln!(
209
+
ui.warning_default(),
210
+
"No branch matching `{branch}` found on any specified/configured remote",
211
+
)?;
210
212
}
211
-
}
212
-
213
-
if !missing_branches.is_empty() {
214
-
writeln!(
215
-
ui.warning_default(),
216
-
"No branch matching {} found on any specified/configured remote",
217
-
missing_branches.iter().map(|b| format!("`{b}`")).join(", ")
218
-
)?;
219
213
}
220
214
221
215
Ok(())
+1
-7
cli/src/commands/resolve.rs
+1
-7
cli/src/commands/resolve.rs
···
12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
-
use clap_complete::ArgValueCandidates;
16
15
use clap_complete::ArgValueCompleter;
17
16
use itertools::Itertools as _;
18
17
use jj_lib::object_id::ObjectId as _;
···
62
61
///
63
62
/// The built-in merge tools `:ours` and `:theirs` can be used to choose
64
63
/// side #1 and side #2 of the conflict respectively.
65
-
#[arg(
66
-
long,
67
-
conflicts_with = "list",
68
-
value_name = "NAME",
69
-
add = ArgValueCandidates::new(complete::merge_tools),
70
-
)]
64
+
#[arg(long, conflicts_with = "list", value_name = "NAME")]
71
65
tool: Option<String>,
72
66
/// Only resolve conflicts in these paths. You can use the `--list` argument
73
67
/// to find paths to use here.
+1
-6
cli/src/commands/restore.rs
+1
-6
cli/src/commands/restore.rs
···
14
14
15
15
use std::io::Write as _;
16
16
17
-
use clap_complete::ArgValueCandidates;
18
17
use clap_complete::ArgValueCompleter;
19
18
use indoc::formatdoc;
20
19
use itertools::Itertools as _;
···
97
96
#[arg(long, short)]
98
97
interactive: bool,
99
98
/// Specify diff editor to be used (implies --interactive)
100
-
#[arg(
101
-
long,
102
-
value_name = "NAME",
103
-
add = ArgValueCandidates::new(complete::diff_editors),
104
-
)]
99
+
#[arg(long, value_name = "NAME")]
105
100
tool: Option<String>,
106
101
/// Preserve the content (not the diff) when rebasing descendants
107
102
#[arg(long)]
+11
-15
cli/src/commands/split.rs
+11
-15
cli/src/commands/split.rs
···
14
14
use std::collections::HashMap;
15
15
use std::io::Write as _;
16
16
17
-
use clap_complete::ArgValueCandidates;
18
17
use clap_complete::ArgValueCompleter;
19
18
use jj_lib::backend::CommitId;
20
19
use jj_lib::commit::Commit;
···
50
49
///
51
50
/// Starts a [diff editor] on the changes in the revision. Edit the right side
52
51
/// of the diff until it has the content you want in the new revision. Once
53
-
/// you close the editor, your edited content will be put in a new revision
54
-
/// before the original revision, while the remaining changes will replace the
55
-
/// original revision.
52
+
/// you close the editor, your edited content will replace the previous
53
+
/// revision. The remaining changes will be put in a new revision on top.
56
54
///
57
55
/// [diff editor]:
58
56
/// https://jj-vcs.github.io/jj/latest/config/#editing-diffs
···
72
70
#[arg(long, short)]
73
71
interactive: bool,
74
72
/// Specify diff editor to be used (implies --interactive)
75
-
#[arg(
76
-
long,
77
-
value_name = "NAME",
78
-
add = ArgValueCandidates::new(complete::diff_editors),
79
-
)]
73
+
#[arg(long, value_name = "NAME")]
80
74
tool: Option<String>,
81
75
/// The revision to split
82
76
#[arg(
···
225
219
// Prompt the user to select the changes they want for the first commit.
226
220
let target = select_diff(ui, &tx, &target_commit, &matcher, &diff_selector)?;
227
221
228
-
let legacy_bookmark_behavior =
229
-
!use_move_flags && tx.settings().get_bool("split.legacy-bookmark-behavior")?;
230
-
231
222
// Create the first commit, which includes the changes selected by the user.
232
223
let first_commit = {
233
224
let mut commit_builder = tx.repo_mut().rewrite_commit(&target.commit).detach();
234
225
commit_builder.set_tree_id(target.selected_tree.id());
235
-
if !legacy_bookmark_behavior {
226
+
if use_move_flags {
236
227
commit_builder
237
228
// Generate a new change id so that the commit being split doesn't
238
229
// become divergent.
···
279
270
commit_builder
280
271
.set_parents(parents)
281
272
.set_tree_id(new_tree.id());
282
-
if legacy_bookmark_behavior {
273
+
if !use_move_flags {
283
274
commit_builder
284
275
// Generate a new change id so that the commit being split doesn't
285
276
// become divergent.
···
416
407
tx.repo_mut()
417
408
.transform_descendants(vec![target.commit.id().clone()], |mut rewriter| {
418
409
num_rebased += 1;
419
-
if parallel {
410
+
if parallel && legacy_bookmark_behavior {
411
+
// The old_parent is the second commit due to the rewrite above.
420
412
rewriter
421
413
.replace_parent(second_commit.id(), [first_commit.id(), second_commit.id()]);
414
+
} else if parallel {
415
+
rewriter.replace_parent(first_commit.id(), [first_commit.id(), second_commit.id()]);
416
+
} else {
417
+
rewriter.replace_parent(first_commit.id(), [second_commit.id()]);
422
418
}
423
419
rewriter.rebase()?.write()?;
424
420
Ok(())
+3
-162
cli/src/commands/squash.rs
+3
-162
cli/src/commands/squash.rs
···
12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
-
use clap_complete::ArgValueCandidates;
16
15
use clap_complete::ArgValueCompleter;
17
16
use indoc::formatdoc;
18
17
use itertools::Itertools as _;
···
23
22
use jj_lib::repo::Repo as _;
24
23
use jj_lib::rewrite;
25
24
use jj_lib::rewrite::CommitWithSelection;
26
-
use jj_lib::rewrite::SquashOptions;
27
25
use tracing::instrument;
28
26
29
27
use crate::cli_util::CommandHelper;
···
101
99
#[arg(long, short)]
102
100
interactive: bool,
103
101
/// Specify diff editor to be used (implies --interactive)
104
-
#[arg(
105
-
long,
106
-
value_name = "NAME",
107
-
add = ArgValueCandidates::new(complete::diff_editors),
108
-
)]
102
+
#[arg(long, value_name = "NAME")]
109
103
tool: Option<String>,
110
104
/// Move only changes to these paths (instead of all paths)
111
105
#[arg(
···
118
112
/// The source revision will not be abandoned
119
113
#[arg(long, short)]
120
114
keep_emptied: bool,
121
-
/// Preserve the content (not the diff) when rebasing descendants of the
122
-
/// source and target commits
123
-
///
124
-
/// Only the snapshots of the `--from` and the `--into` commits will be
125
-
/// modified.
126
-
///
127
-
/// If you'd like to preserve the content of *only* the target's descendants
128
-
/// (or *only* the source's), consider using `jj rebase -r` or `jj
129
-
/// duplicate` before squashing.
130
-
//
131
-
// See "NOTE: Not implementing `--restore-{target,source}-descendants`" in
132
-
// squash.rs.
133
-
//
134
-
// TODO: Once it's implemented, we should recommend `jj rebase -r
135
-
// --restore-descendants` instead of `jj duplicate`, since you actually
136
-
// would need to `squash` twice with `duplicate`.
137
-
#[arg(long)]
138
-
restore_descendants: bool,
139
115
}
140
116
141
-
// NOTE: Not implementing `--restore-{target,source}-descendants`
142
-
// --------------------------------------------------------------
143
-
//
144
-
// We have `jj squash --restore-descendants --from X --into Y` preserve the
145
-
// snapshots of both the descendants of `X` and those of the descendants of `Y`.
146
-
// This behavior makes it simple to understand; it does the same thing to the
147
-
// child of any commit `jj squash` rewrites. As @yuja pointed out it could even
148
-
// be a global flag that would apply to any command that rewrites commits.
149
-
//
150
-
// In this note, we explain why we choose not to have a flag for `jj squash`
151
-
// that preserves *only* the descendants of the source (call it
152
-
// `--restore-source-descendants`) or a similar `--restore-target-descendants`
153
-
// flag, even though they might seem easy to implement at a glance.
154
-
//
155
-
// (The same argument applies to `jj rebase --restore-???-descendants`.)
156
-
//
157
-
// Firstly, such extra flags seem to only be useful in rare cases. If needed,
158
-
// they can be simulated. Instead of `squash --restore-target-descendants`, you
159
-
// could do `jj rebase -r X -d all:X-; jj squash --restore-descendants --from X
160
-
// --into Y`. Instead of `squash --restore-source-descendants`, you could do `jj
161
-
// duplicate -r X; jj squash --restore-descendants --from copy_of_X --into Y; jj
162
-
// abandon --restore-descendants X`. (TODO: When `jj rebase -r
163
-
// --restore-descendants` is implemented, this will become 2 commands instead of
164
-
// 3).
165
-
//
166
-
// Secondly, the behavior of these flags would get confusing in corner cases,
167
-
// when the target is an ancestor or descendant of the source, or for ancestors
168
-
// of merge commits. For example, consider this commit graph with merge commit
169
-
// `Z` where `A` is *not* empty (thanks to @lilyball for suggesting the merge
170
-
// commit example):
171
-
//
172
-
// ```
173
-
// A -> X -
174
-
// \ (Example I)
175
-
// B -> Y --->Z
176
-
// ```
177
-
//
178
-
// The behavior of `jj squash --from A --into B --restore-descendants` is easy
179
-
// to understand: the snapshots of `X` and `Y` remain the same, and all of their
180
-
// descendants also remain the same by normal rebasing rules.
181
-
//
182
-
// If we allowed `jj squash --from A --into B --restore-target-descendants`,
183
-
// what should it mean? It seems clear that `X`'s snapshot should remain the
184
-
// same, and `X`'s will change. However, should `Z`'s snapshot change? If we
185
-
// follow the logic that Z had one of its parents change and the other stay the
186
-
// same, it seems that yes, it should. This is also what the equivalence with
187
-
// `jj rebase -r A -d A-; jj squash --from A --into B --restore-descendants`
188
-
// would imply.
189
-
//
190
-
// (A contrarian mind could argue that `Z`'s snapshot should be preserved since
191
-
// `Z` is a descendant of the target `B`. We'll put this thought aside for a
192
-
// moment and keep going, to see how things get even more confusing.)
193
-
//
194
-
// Now, let's pretend we squashed `X` and `Y` into `Z` and ask the same
195
-
// question. Our graph is now:
196
-
//
197
-
// ```
198
-
// A -
199
-
// \ (Example II)
200
-
// B --->Z
201
-
// ```
202
-
//
203
-
// By the logic above, the snapshot of `Z` will again change after `jj squash
204
-
// --from A --into B --restore-target-descendants`. This is unsatisfying and
205
-
// would probably be unexpected, since `Z` is a direct child of the target
206
-
// commit `B`, so the user might expect its snapshot to be preserved.
207
-
//
208
-
// Now, there are a few options:
209
-
//
210
-
// 1. Allow the confusing but seemingly correct definition of
211
-
// `--restore-target-descendants` as above.
212
-
// 2. Allow `--restore-target-descendants`, but forbid it in some set of
213
-
// situations we deem too confusing.
214
-
// 3. Have the effect of `jj squash --from A --into B
215
-
// --restore-target-descendants` on `Z`'s snapshot differ between Example I
216
-
// and Example II. In other words, the behavior will depend on whether there
217
-
// are commits (even if they are empty commits!) between `A` and `Z`, or
218
-
// between `B` and `Z`.
219
-
// 4. Declare that in both Example I and Example II above, the snapshot of `Z`
220
-
// should be preserved.
221
-
//
222
-
// The first problem with this (and with option 3 above) would be that
223
-
// `--restore-target-descendants` would now be equivalent to a rebase
224
-
// followed by `squash --restore-descendants` *almost* always, but would
225
-
// differ in corner cases.
226
-
//
227
-
// Perhaps more importantly, this would break the important property of `jj
228
-
// squash --restore-target-descendants` that its difference from the
229
-
// behavior of normal `jj squash` is local; affects only the direct children
230
-
// of the modified commits. All others can normally be rebased by normal
231
-
// `jj` rules.
232
-
//
233
-
// If `jj squash --restore-target-descendants` preserved the snapshot of `Z`
234
-
// even if there are 100 commit between it and `A`, this would change its
235
-
// diff relative to its parents, possibly without any awareness from the
236
-
// user that this happened or that `Z` even existed.
237
-
// 5. Do not provide `--restore-target-descendants` ourselves, and recommend
238
-
// that the user manually does `jj rebase -r X -d all:X-; jj squash
239
-
// --restore-descendants --from X --into Y` if they really need it.
240
-
//
241
-
// The last option seems easiest. It also has the advantage of requiring fewer
242
-
// tests and being the simplest to maintain.
243
-
//
244
-
// Aside: the merge example is probably the easiest to understand and the most
245
-
// problematic, but for `X -> A -> B -> C -> D`, both `jj squash --from C
246
-
// --into A --restore-target-descendants` and `jj squash --from A --into C
247
-
// --restore-source-descendants` have similar problems.
248
-
249
117
#[instrument(skip_all)]
250
118
pub(crate) fn cmd_squash(
251
119
ui: &mut Ui,
···
298
166
.check_rewritable(sources.iter().chain(std::iter::once(&destination)).ids())?;
299
167
300
168
let mut tx = workspace_command.start_transaction();
301
-
let tx_description = format!(
302
-
"squash commits into {}{}",
303
-
destination.id().hex(),
304
-
if args.restore_descendants {
305
-
" while preserving descendant contents"
306
-
} else {
307
-
""
308
-
}
309
-
);
169
+
let tx_description = format!("squash commits into {}", destination.id().hex());
310
170
let source_commits = select_diff(&tx, &sources, &destination, &matcher, &diff_selector)?;
311
171
if let Some(squashed) = rewrite::squash_commits(
312
172
tx.repo_mut(),
313
173
&source_commits,
314
174
&destination,
315
-
SquashOptions {
316
-
keep_emptied: args.keep_emptied,
317
-
// See "NOTE: Not implementing `--restore-{target,source}-descendants`" in
318
-
// squash.rs.
319
-
restore_descendants: args.restore_descendants,
320
-
},
175
+
args.keep_emptied,
321
176
)? {
322
177
let mut commit_builder = squashed.commit_builder.detach();
323
178
let new_description = match description {
···
365
220
};
366
221
commit_builder.set_description(new_description);
367
222
commit_builder.write(tx.repo_mut())?;
368
-
369
-
if args.restore_descendants {
370
-
// If !args.restore_descendants, the corresponding steps are done inside
371
-
// tx.finish()
372
-
let num_reparented = tx.repo_mut().reparent_descendants()?;
373
-
if let Some(mut formatter) = ui.status_formatter() {
374
-
writeln!(
375
-
formatter,
376
-
"Rebased {num_reparented} descendant commits (while preserving their content)",
377
-
)?;
378
-
}
379
-
}
380
223
} else {
381
224
if diff_selector.is_interactive() {
382
225
return Err(user_error("No changes selected"));
···
398
241
}
399
242
}
400
243
}
401
-
// TODO: Show the "Rebase NNN descendant commits message", add " (while
402
-
// preserving their content)" in the --restore-descendants mode
403
244
tx.finish(ui, tx_description)?;
404
245
Ok(())
405
246
}
+1
-49
cli/src/complete.rs
+1
-49
cli/src/complete.rs
···
34
34
use crate::config::ConfigArgKind;
35
35
use crate::config::ConfigEnv;
36
36
use crate::config::CONFIG_SCHEMA;
37
-
use crate::merge_tools::configured_merge_tools;
38
-
use crate::merge_tools::MergeEditor;
39
37
use crate::revset_util::load_revset_aliases;
40
38
use crate::ui::Ui;
41
39
···
476
474
.collect())
477
475
})
478
476
}
479
-
pub fn merge_tools() -> Vec<CompletionCandidate> {
480
-
with_jj(|_, settings| {
481
-
Ok([":builtin", ":ours", ":theirs"]
482
-
.into_iter()
483
-
.chain(
484
-
configured_merge_tools(settings)
485
-
.filter(|name| MergeEditor::dummy_with_name(name, settings).is_ok()),
486
-
)
487
-
.map(CompletionCandidate::new)
488
-
.collect())
489
-
})
490
-
}
491
-
492
-
/// Approximate list of known diff editors
493
-
///
494
-
/// Diff tools can be used without configuration. Some merge tools that are
495
-
/// configured for 3-way merging may not work for diffing/diff editing, and we
496
-
/// can't tell which these are. So, this not reliable, but probably good enough
497
-
/// for command-line completion.
498
-
pub fn diff_editors() -> Vec<CompletionCandidate> {
499
-
let builtin_format_kinds: Vec<String> = crate::diff_util::BuiltinFormatKind::ALL_VARIANTS
500
-
.iter()
501
-
.map(|kind| format!(":{}", kind.to_arg_name()))
502
-
.collect();
503
-
with_jj(|_, settings| {
504
-
Ok(std::iter::once(":builtin")
505
-
.chain(builtin_format_kinds.iter().map(|s| s.as_str()))
506
-
.chain(configured_merge_tools(settings))
507
-
.map(CompletionCandidate::new)
508
-
.collect())
509
-
})
510
-
}
511
-
512
-
/// Approximate list of known diff tools
513
-
///
514
-
/// Diff tools can be used without configuration. Some merge tools that are
515
-
/// configured for 3-way merging may not work for diffing/diff editing, and we
516
-
/// can't tell which these are. So, this not reliable, but probably good enough
517
-
/// for command-line completion.
518
-
pub fn diff_tools() -> Vec<CompletionCandidate> {
519
-
with_jj(|_, settings| {
520
-
Ok(configured_merge_tools(settings)
521
-
.map(CompletionCandidate::new)
522
-
.collect())
523
-
})
524
-
}
525
477
526
478
fn config_keys_rec(
527
479
prefix: ConfigNamePathBuf,
···
943
895
.and_then(dunce::canonicalize)
944
896
.map_err(user_error)?;
945
897
// No config migration for completion. Simply ignore deprecated variables.
946
-
let mut config_env = ConfigEnv::from_environment();
898
+
let mut config_env = ConfigEnv::from_environment(&ui);
947
899
let maybe_cwd_workspace_loader = DefaultWorkspaceLoaderFactory.create(find_workspace_dir(&cwd));
948
900
let _ = config_env.reload_user_config(&mut raw_config);
949
901
if let Ok(loader) = &maybe_cwd_workspace_loader {
+1
-1
cli/src/config/misc.toml
+1
-1
cli/src/config/misc.toml
+34
-5
cli/src/config.rs
+34
-5
cli/src/config.rs
···
43
43
use crate::command_error::config_error_with_message;
44
44
use crate::command_error::CommandError;
45
45
use crate::text_util;
46
+
use crate::ui::Ui;
46
47
47
48
// TODO(#879): Consider generating entire schema dynamically vs. static file.
48
49
pub const CONFIG_SCHEMA: &str = include_str!("config-schema.json");
···
192
193
fn as_path(&self) -> &Path {
193
194
&self.path
194
195
}
195
-
196
196
fn exists(&self) -> bool {
197
197
match self.state {
198
198
ConfigPathState::Exists => true,
···
218
218
#[derive(Clone, Default, Debug)]
219
219
struct UnresolvedConfigEnv {
220
220
config_dir: Option<PathBuf>,
221
+
// TODO: remove after jj 0.35
221
222
macos_legacy_config_dir: Option<PathBuf>,
222
223
home_dir: Option<PathBuf>,
223
224
jj_config: Option<String>,
224
225
}
225
226
226
227
impl UnresolvedConfigEnv {
227
-
fn resolve(self) -> Vec<ConfigPath> {
228
+
fn resolve(self, ui: &Ui) -> Vec<ConfigPath> {
228
229
if let Some(paths) = self.jj_config {
229
230
return split_paths(&paths)
230
231
.filter(|path| !path.as_os_str().is_empty())
···
283
284
284
285
if let Some(path) = legacy_platform_config_path {
285
286
if path.exists() {
287
+
Self::warn_for_deprecated_path(
288
+
ui,
289
+
path.as_path(),
290
+
"~/Library/Application Support/jj",
291
+
"~/.config/jj",
292
+
);
286
293
paths.push(path);
287
294
}
288
295
}
289
296
if let Some(path) = legacy_platform_config_dir {
290
297
if path.exists() {
298
+
Self::warn_for_deprecated_path(
299
+
ui,
300
+
path.as_path(),
301
+
"~/Library/Application Support/jj",
302
+
"~/.config/jj",
303
+
);
291
304
paths.push(path);
292
305
}
293
306
}
294
307
295
308
paths
296
309
}
310
+
311
+
fn warn_for_deprecated_path(ui: &Ui, path: &Path, old: &str, new: &str) {
312
+
let _ = indoc::writedoc!(
313
+
ui.warning_default(),
314
+
r"
315
+
Deprecated configuration file `{}`.
316
+
Configuration files in `{old}` are deprecated, and support will be removed in a future release.
317
+
Instead, move your configuration files to `{new}`.
318
+
",
319
+
path.display(),
320
+
);
321
+
}
297
322
}
298
323
299
324
#[derive(Clone, Debug)]
···
307
332
308
333
impl ConfigEnv {
309
334
/// Initializes configuration loader based on environment variables.
310
-
pub fn from_environment() -> Self {
335
+
pub fn from_environment(ui: &Ui) -> Self {
311
336
let config_dir = etcetera::choose_base_strategy()
312
337
.ok()
313
338
.map(|s| s.config_dir());
···
322
347
// Library/Preferences is supposed to be exclusively plists
323
348
s.data_dir()
324
349
})
350
+
.filter(|data_dir| {
351
+
// User might've purposefully set their config dir to the deprecated one
352
+
Some(data_dir) != config_dir.as_ref()
353
+
})
325
354
} else {
326
355
None
327
356
};
···
341
370
ConfigEnv {
342
371
home_dir,
343
372
repo_path: None,
344
-
user_config_paths: env.resolve(),
373
+
user_config_paths: env.resolve(ui),
345
374
repo_config_path: None,
346
375
command: None,
347
376
}
···
1728
1757
ConfigEnv {
1729
1758
home_dir,
1730
1759
repo_path: None,
1731
-
user_config_paths: env.resolve(),
1760
+
user_config_paths: env.resolve(&Ui::null()),
1732
1761
repo_config_path: None,
1733
1762
command: None,
1734
1763
}
+3
-19
cli/src/diff_util.rs
+3
-19
cli/src/diff_util.rs
···
23
23
24
24
use bstr::BStr;
25
25
use bstr::BString;
26
-
use clap_complete::ArgValueCandidates;
27
26
use futures::executor::block_on_stream;
28
27
use futures::stream::BoxStream;
29
28
use futures::StreamExt as _;
···
125
124
///
126
125
/// A builtin format can also be specified as `:<name>`. For example,
127
126
/// `--tool=:git` is equivalent to `--git`.
128
-
#[arg(
129
-
long,
130
-
add = ArgValueCandidates::new(crate::complete::diff_tools),
131
-
)]
127
+
#[arg(long)]
132
128
pub tool: Option<String>,
133
129
/// Number of lines of context to show
134
130
#[arg(long)]
···
156
152
}
157
153
158
154
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
159
-
pub enum BuiltinFormatKind {
155
+
enum BuiltinFormatKind {
160
156
Summary,
161
157
Stat,
162
158
Types,
···
166
162
}
167
163
168
164
impl BuiltinFormatKind {
169
-
// Alternatively, we could use or vendor one of the crates `strum`,
170
-
// `enum-iterator`, or `variant_count` (for a check that the length of the array
171
-
// is correct). The latter is very simple and is also a nightly feature.
172
-
pub const ALL_VARIANTS: &[BuiltinFormatKind] = &[
173
-
Self::Summary,
174
-
Self::Stat,
175
-
Self::Types,
176
-
Self::NameOnly,
177
-
Self::Git,
178
-
Self::ColorWords,
179
-
];
180
-
181
165
fn from_name(name: &str) -> Result<Self, String> {
182
166
match name {
183
167
"summary" => Ok(Self::Summary),
···
221
205
}
222
206
}
223
207
224
-
pub fn to_arg_name(self) -> &'static str {
208
+
fn to_arg_name(self) -> &'static str {
225
209
match self {
226
210
Self::Summary => "summary",
227
211
Self::Stat => "stat",
+6
-22
cli/src/merge_tools/mod.rs
+6
-22
cli/src/merge_tools/mod.rs
···
207
207
}
208
208
}
209
209
210
-
/// List configured merge tools (diff editors, diff tools, merge editors)
211
-
pub fn configured_merge_tools(settings: &UserSettings) -> impl Iterator<Item = &str> {
212
-
settings.table_keys("merge-tools")
213
-
}
214
-
215
210
/// Loads external diff/merge tool options from `[merge-tools.<name>]`.
216
211
pub fn get_external_tool_config(
217
212
settings: &UserSettings,
···
383
378
let tool = MergeTool::get_tool_config(settings, name)?
384
379
.unwrap_or_else(|| MergeTool::external(ExternalMergeTool::with_program(name)));
385
380
Self::new_inner(name, tool, path_converter, conflict_marker_style)
386
-
}
387
-
388
-
/// For the purposes of testing or checking basic config
389
-
pub fn dummy_with_name(
390
-
name: &str,
391
-
settings: &UserSettings,
392
-
) -> Result<Self, MergeToolConfigError> {
393
-
Self::with_name(
394
-
name,
395
-
settings,
396
-
RepoPathUiConverter::Fs {
397
-
cwd: "".into(),
398
-
base: "".into(),
399
-
},
400
-
ConflictMarkerStyle::Diff,
401
-
)
402
381
}
403
382
404
383
/// Loads the default 3-way merge editor from the settings.
···
786
765
let get = |name, config_text| {
787
766
let config = config_from_string(config_text);
788
767
let settings = UserSettings::from_config(config).unwrap();
789
-
MergeEditor::dummy_with_name(name, &settings).map(|editor| editor.tool)
768
+
let path_converter = RepoPathUiConverter::Fs {
769
+
cwd: "".into(),
770
+
base: "".into(),
771
+
};
772
+
MergeEditor::with_name(name, &settings, path_converter, ConflictMarkerStyle::Diff)
773
+
.map(|editor| editor.tool)
790
774
};
791
775
792
776
insta::assert_debug_snapshot!(get(":builtin", "").unwrap(), @"Builtin");
+1
-15
cli/tests/cli-reference@.md.snap
+1
-15
cli/tests/cli-reference@.md.snap
···
906
906
* `-p`, `--patch` โ Show patch compared to the previous version of this change
907
907
908
908
If the previous version has different parents, it will be temporarily rebased to the parents of the new version, so the diff is not contaminated by unrelated changes.
909
-
* `--diff-snapshots` โ Changes the behavior of `--patch`, `--git`, etc to show diffs from rebases
910
-
911
-
Implies `--patch` if no other diff format is requested.
912
-
913
-
Normally, `jj evolog -p` shows a so-called "interdiff", temporarily rebasing the versions of a revision to the same parents, in order to omit differences in the file contents that are caused by rebases.
914
-
915
-
This option disables this behavior, and shows diffs between the contents of the different versions without modification (as snapshots).
916
-
917
-
Sometimes, `--diff-snapshots` can show fewer differences to be shown. For example, let's say the current revision is not empty and we perform `jj squash --keep-empty -r @` to make it empty. Then, `jj evolog -p --diff-snapshots` will not show any changes since the contents of the files in the current revision did not change. However, `jj evolog -p` will show a change, representing the fact that a non-empty revision became empty.
918
909
* `-s`, `--summary` โ For each path, show only whether it was modified, added, or deleted
919
910
* `--stat` โ Show a histogram of the changes
920
911
* `--types` โ For each path, show only its type before and after
···
2470
2461
2471
2462
Split a revision in two
2472
2463
2473
-
Starts a [diff editor] on the changes in the revision. Edit the right side of the diff until it has the content you want in the new revision. Once you close the editor, your edited content will be put in a new revision before the original revision, while the remaining changes will replace the original revision.
2464
+
Starts a [diff editor] on the changes in the revision. Edit the right side of the diff until it has the content you want in the new revision. Once you close the editor, your edited content will replace the previous revision. The remaining changes will be put in a new revision on top.
2474
2465
2475
2466
[diff editor]: https://jj-vcs.github.io/jj/latest/config/#editing-diffs
2476
2467
···
2533
2524
* `-i`, `--interactive` โ Interactively choose which parts to squash
2534
2525
* `--tool <NAME>` โ Specify diff editor to be used (implies --interactive)
2535
2526
* `-k`, `--keep-emptied` โ The source revision will not be abandoned
2536
-
* `--restore-descendants` โ Preserve the content (not the diff) when rebasing descendants of the source and target commits
2537
-
2538
-
Only the snapshots of the `--from` and the `--into` commits will be modified.
2539
-
2540
-
If you'd like to preserve the content of *only* the target's descendants (or *only* the source's), consider using `jj rebase -r` or `jj duplicate` before squashing.
2541
2527
2542
2528
2543
2529
+1
-1
cli/tests/test_commit_command.rs
+1
-1
cli/tests/test_commit_command.rs
···
298
298
โ 000000000000
299
299
[EOF]
300
300
------- stderr -------
301
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
301
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
302
302
[EOF]
303
303
"#);
304
304
insta::assert_snapshot!(
-61
cli/tests/test_completion.rs
-61
cli/tests/test_completion.rs
···
1089
1089
");
1090
1090
}
1091
1091
1092
-
#[test]
1093
-
fn test_merge_tools() {
1094
-
let mut test_env = TestEnvironment::default();
1095
-
test_env.add_env_var("COMPLETE", "fish");
1096
-
let dir = test_env.env_root();
1097
-
1098
-
let output = test_env.run_jj_in(dir, ["--", "jj", "diff", "--tool", ""]);
1099
-
insta::assert_snapshot!(output, @r"
1100
-
diffedit3
1101
-
diffedit3-ssh
1102
-
difft
1103
-
kdiff3
1104
-
meld
1105
-
meld-3
1106
-
mergiraf
1107
-
smerge
1108
-
vimdiff
1109
-
vscode
1110
-
vscodium
1111
-
[EOF]
1112
-
");
1113
-
// Includes :builtin
1114
-
let output = test_env.run_jj_in(dir, ["--", "jj", "diffedit", "--tool", ""]);
1115
-
insta::assert_snapshot!(output, @r"
1116
-
:builtin
1117
-
:summary
1118
-
:stat
1119
-
:types
1120
-
:name-only
1121
-
:git
1122
-
:color-words
1123
-
diffedit3
1124
-
diffedit3-ssh
1125
-
difft
1126
-
kdiff3
1127
-
meld
1128
-
meld-3
1129
-
mergiraf
1130
-
smerge
1131
-
vimdiff
1132
-
vscode
1133
-
vscodium
1134
-
[EOF]
1135
-
");
1136
-
// Only includes configured merge editors
1137
-
let output = test_env.run_jj_in(dir, ["--", "jj", "resolve", "--tool", ""]);
1138
-
insta::assert_snapshot!(output, @r"
1139
-
:builtin
1140
-
:ours
1141
-
:theirs
1142
-
kdiff3
1143
-
meld
1144
-
mergiraf
1145
-
smerge
1146
-
vimdiff
1147
-
vscode
1148
-
vscodium
1149
-
[EOF]
1150
-
");
1151
-
}
1152
-
1153
1092
fn create_commit(
1154
1093
work_dir: &TestWorkDir,
1155
1094
name: &str,
+20
cli/tests/test_config_command.rs
+20
cli/tests/test_config_command.rs
···
971
971
insta::assert_snapshot!(output, @r"
972
972
Make sure I can pick this up
973
973
[EOF]
974
+
------- stderr -------
975
+
Warning: Deprecated configuration file `$TEST_ENV/home/Library/Application Support/jj/config.toml`.
976
+
Configuration files in `~/Library/Application Support/jj` are deprecated, and support will be removed in a future release.
977
+
Instead, move your configuration files to `~/.config/jj`.
978
+
[EOF]
979
+
");
980
+
981
+
// if XDG_CONFIG_HOME is ~/Library/Application Support,
982
+
// you shouldn't get a warning
983
+
let output = test_env.run_jj_with(|cmd| {
984
+
cmd.env_remove("JJ_CONFIG")
985
+
.env(
986
+
"XDG_CONFIG_HOME",
987
+
test_env.home_dir().join("Library/Application Support"),
988
+
)
989
+
.args(["config", "get", "foo.bar"])
990
+
});
991
+
insta::assert_snapshot!(output, @r"
992
+
Make sure I can pick this up
993
+
[EOF]
974
994
");
975
995
976
996
// if you set JJ_CONFIG, you shouldn't get a warning
+2
-2
cli/tests/test_describe_command.rs
+2
-2
cli/tests/test_describe_command.rs
···
618
618
let output = work_dir.run_jj(["describe"]);
619
619
insta::assert_snapshot!(output, @r#"
620
620
------- stderr -------
621
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
621
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
622
622
Working copy (@) now at: qpvuntsm 7276dfff TESTED=TODO
623
623
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
624
624
[EOF]
···
639
639
let output = work_dir.run_jj(["describe", "--no-edit", "--reset-author"]);
640
640
insta::assert_snapshot!(output, @r#"
641
641
------- stderr -------
642
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
642
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
643
643
Working copy (@) now at: kkmpptxz 7118bcb8 (empty) (no description set)
644
644
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
645
645
[EOF]
+3
-3
cli/tests/test_diff_command.rs
+3
-3
cli/tests/test_diff_command.rs
···
253
253
-4
254
254
[EOF]
255
255
------- stderr -------
256
-
Warning: Deprecated config: ui.diff.format is updated to ui.diff-formatter = ":git"
256
+
Warning: Deprecated CLI-provided config: ui.diff.format is updated to ui.diff-formatter = ":git"
257
257
[EOF]
258
258
"#);
259
259
···
3138
3138
file3
3139
3139
[EOF]
3140
3140
------- stderr -------
3141
-
Warning: Deprecated config: ui.diff.tool is renamed to ui.diff-formatter
3142
-
Warning: Deprecated config: ui.diff.format is deleted (superseded by ui.diff-formatter)
3141
+
Warning: Deprecated CLI-provided config: ui.diff.tool is renamed to ui.diff-formatter
3142
+
Warning: Deprecated CLI-provided config: ui.diff.format is deleted (superseded by ui.diff-formatter)
3143
3143
[EOF]
3144
3144
");
3145
3145
-46
cli/tests/test_evolog_command.rs
-46
cli/tests/test_evolog_command.rs
···
171
171
-- operation e0f8e58b3800 (2001-02-03 08:05:08) new empty commit
172
172
[EOF]
173
173
");
174
-
175
-
// With `--diff-snapshots`, the rebase does show a diff
176
-
// TODO: Bug, not implemented yet with --no-graph
177
-
let output = work_dir.run_jj(["evolog", "--no-graph", "--git", "--diff-snapshots"]);
178
-
insta::assert_snapshot!(output, @r"
179
-
rlvkpnrz test.user@example.com 2001-02-03 08:05:10 33c10ace
180
-
my description
181
-
-- operation 3499115d3831 (2001-02-03 08:05:10) snapshot working copy
182
-
diff --git a/file1 b/file1
183
-
index 0000000000..2ab19ae607 100644
184
-
--- a/file1
185
-
+++ b/file1
186
-
@@ -1,7 +1,1 @@
187
-
-<<<<<<< Conflict 1 of 1
188
-
-%%%%%%% Changes from base to side #1
189
-
--foo
190
-
-+++++++ Contents of side #2
191
-
-foo
192
-
-bar
193
-
->>>>>>> Conflict 1 of 1 ends
194
-
+resolved
195
-
rlvkpnrz hidden test.user@example.com 2001-02-03 08:05:09 7f56b2a0 conflict
196
-
my description
197
-
-- operation eb87ec366530 (2001-02-03 08:05:09) rebase commit 51e08f95160c897080d035d330aead3ee6ed5588
198
-
rlvkpnrz hidden test.user@example.com 2001-02-03 08:05:09 51e08f95
199
-
my description
200
-
-- operation 18a971ce330a (2001-02-03 08:05:09) snapshot working copy
201
-
diff --git a/file1 b/file1
202
-
index 257cc5642c..3bd1f0e297 100644
203
-
--- a/file1
204
-
+++ b/file1
205
-
@@ -1,1 +1,2 @@
206
-
foo
207
-
+bar
208
-
diff --git a/file2 b/file2
209
-
new file mode 100644
210
-
index 0000000000..257cc5642c
211
-
--- /dev/null
212
-
+++ b/file2
213
-
@@ -0,0 +1,1 @@
214
-
+foo
215
-
rlvkpnrz hidden test.user@example.com 2001-02-03 08:05:08 b955b72e
216
-
(empty) my description
217
-
-- operation e0f8e58b3800 (2001-02-03 08:05:08) new empty commit
218
-
[EOF]
219
-
");
220
174
}
221
175
222
176
#[test]
+2
-1
cli/tests/test_git_fetch.rs
+2
-1
cli/tests/test_git_fetch.rs
···
1038
1038
]);
1039
1039
insta::assert_snapshot!(output, @r"
1040
1040
------- stderr -------
1041
-
Warning: No branch matching `noexist1`, `noexist2` found on any specified/configured remote
1041
+
Warning: No branch matching `noexist1` found on any specified/configured remote
1042
+
Warning: No branch matching `noexist2` found on any specified/configured remote
1042
1043
Nothing changed.
1043
1044
[EOF]
1044
1045
");
+140
-140
cli/tests/test_split_command.rs
+140
-140
cli/tests/test_split_command.rs
···
73
73
let output = work_dir.run_jj(["split", "file2"]);
74
74
insta::assert_snapshot!(output, @r"
75
75
------- stderr -------
76
-
Selected changes : zsuskuln 8a73f71d (no description set)
77
-
Remaining changes: qpvuntsm c4d8ebac (no description set)
78
-
Working copy (@) now at: qpvuntsm c4d8ebac (no description set)
79
-
Parent commit (@-) : zsuskuln 8a73f71d (no description set)
76
+
Selected changes : qpvuntsm 6dbc7747 (no description set)
77
+
Remaining changes: zsuskuln 42cbbc02 (no description set)
78
+
Working copy (@) now at: zsuskuln 42cbbc02 (no description set)
79
+
Parent commit (@-) : qpvuntsm 6dbc7747 (no description set)
80
80
[EOF]
81
81
");
82
82
insta::assert_snapshot!(
···
92
92
assert!(!test_env.env_root().join("editor1").exists());
93
93
94
94
insta::assert_snapshot!(get_log_output(&work_dir), @r"
95
-
@ qpvuntsmwlqt false
96
-
โ zsuskulnrvyr false
95
+
@ zsuskulnrvyr false
96
+
โ qpvuntsmwlqt false
97
97
โ zzzzzzzzzzzz true
98
98
[EOF]
99
99
");
···
128
128
------- stderr -------
129
129
Warning: All changes have been selected, so the original revision will become empty
130
130
Rebased 1 descendant commits
131
-
Selected changes : znkkpsqq d6e65134 (no description set)
132
-
Remaining changes: zsuskuln aa27eaa3 (empty) (no description set)
133
-
Working copy (@) now at: qpvuntsm e94cab21 (no description set)
134
-
Parent commit (@-) : zsuskuln aa27eaa3 (empty) (no description set)
131
+
Selected changes : qpvuntsm 9fd1c9e1 (no description set)
132
+
Remaining changes: znkkpsqq 41e0da21 (empty) (no description set)
133
+
Working copy (@) now at: zsuskuln a06e40b8 (no description set)
134
+
Parent commit (@-) : znkkpsqq 41e0da21 (empty) (no description set)
135
135
[EOF]
136
136
");
137
137
138
138
insta::assert_snapshot!(get_log_output(&work_dir), @r"
139
-
@ qpvuntsmwlqt false
140
-
โ zsuskulnrvyr true
141
-
โ znkkpsqqskkl false
139
+
@ zsuskulnrvyr false
140
+
โ znkkpsqqskkl true
141
+
โ qpvuntsmwlqt false
142
142
โ zzzzzzzzzzzz true
143
143
[EOF]
144
144
");
···
159
159
------- stderr -------
160
160
Warning: No changes have been selected, so the new revision will be empty
161
161
Rebased 1 descendant commits
162
-
Selected changes : lylxulpl 3d639d71 (empty) (no description set)
163
-
Remaining changes: znkkpsqq 706a0e77 (no description set)
164
-
Working copy (@) now at: qpvuntsm 502cf440 (no description set)
165
-
Parent commit (@-) : znkkpsqq 706a0e77 (no description set)
162
+
Selected changes : qpvuntsm 49416632 (empty) (no description set)
163
+
Remaining changes: lylxulpl 718afbf5 (no description set)
164
+
Working copy (@) now at: zsuskuln 0ed53ee6 (no description set)
165
+
Parent commit (@-) : lylxulpl 718afbf5 (no description set)
166
166
[EOF]
167
167
");
168
168
169
169
insta::assert_snapshot!(get_log_output(&work_dir), @r"
170
-
@ qpvuntsmwlqt false
171
-
โ znkkpsqqskkl false
172
-
โ lylxulplsnyw true
170
+
@ zsuskulnrvyr false
171
+
โ lylxulplsnyw false
172
+
โ qpvuntsmwlqt true
173
173
โ zzzzzzzzzzzz true
174
174
[EOF]
175
175
");
···
207
207
let output = work_dir.run_jj(["split", "file1"]);
208
208
insta::assert_snapshot!(output, @r#"
209
209
------- stderr -------
210
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
211
-
Selected changes : kkmpptxz 530f78ed part 1
212
-
Remaining changes: qpvuntsm 88189e08 part 2
213
-
Working copy (@) now at: qpvuntsm 88189e08 part 2
214
-
Parent commit (@-) : kkmpptxz 530f78ed part 1
210
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
211
+
Selected changes : qpvuntsm c7f7b14b part 1
212
+
Remaining changes: kkmpptxz ac33a5a9 part 2
213
+
Working copy (@) now at: kkmpptxz ac33a5a9 part 2
214
+
Parent commit (@-) : qpvuntsm c7f7b14b part 1
215
215
[EOF]
216
216
"#);
217
217
···
236
236
JJ: Lines starting with "JJ:" (like this one) will be removed.
237
237
"#);
238
238
insta::assert_snapshot!(get_log_output(&work_dir), @r#"
239
-
@ qpvuntsmwlqt false part 2
240
-
โ kkmpptxzrspx false part 1
239
+
@ kkmpptxzrspx false part 2
240
+
โ qpvuntsmwlqt false part 1
241
241
โ zzzzzzzzzzzz true
242
242
[EOF]
243
243
------- stderr -------
244
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
244
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
245
245
[EOF]
246
246
"#);
247
247
}
···
265
265
let output = work_dir.run_jj(["split", "file1"]);
266
266
insta::assert_snapshot!(output, @r#"
267
267
------- stderr -------
268
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
269
-
Selected changes : rlvkpnrz 16dc7e13 TESTED=TODO
270
-
Remaining changes: qpvuntsm f40d53f2 (no description set)
271
-
Working copy (@) now at: qpvuntsm f40d53f2 (no description set)
272
-
Parent commit (@-) : rlvkpnrz 16dc7e13 TESTED=TODO
268
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
269
+
Selected changes : qpvuntsm ff633dcc TESTED=TODO
270
+
Remaining changes: rlvkpnrz b1d20b7e (no description set)
271
+
Working copy (@) now at: rlvkpnrz b1d20b7e (no description set)
272
+
Parent commit (@-) : qpvuntsm ff633dcc TESTED=TODO
273
273
[EOF]
274
274
"#);
275
275
···
291
291
"#);
292
292
assert!(!test_env.env_root().join("editor2").exists());
293
293
insta::assert_snapshot!(get_log_output(&work_dir), @r#"
294
-
@ qpvuntsmwlqt false
295
-
โ rlvkpnrzqnoo false TESTED=TODO
294
+
@ rlvkpnrzqnoo false
295
+
โ qpvuntsmwlqt false TESTED=TODO
296
296
โ zzzzzzzzzzzz true
297
297
[EOF]
298
298
------- stderr -------
299
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
299
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
300
300
[EOF]
301
301
"#);
302
302
}
···
346
346
insta::assert_snapshot!(output, @r"
347
347
------- stderr -------
348
348
Rebased 2 descendant commits
349
-
Selected changes : royxmykx e13e94b9 Add file1
350
-
Remaining changes: qpvuntsm cf8ebbab Add file2
351
-
Working copy (@) now at: kkmpptxz 73a16519 Add file4
352
-
Parent commit (@-) : rlvkpnrz ec4d3a14 Add file3
349
+
Selected changes : qpvuntsm 74306e35 Add file1
350
+
Remaining changes: royxmykx 0a37745e Add file2
351
+
Working copy (@) now at: kkmpptxz 7ee84812 Add file4
352
+
Parent commit (@-) : rlvkpnrz d335bd94 Add file3
353
353
[EOF]
354
354
");
355
-
insta::assert_snapshot!(get_log_output(&work_dir), @r"
355
+
insta::assert_snapshot!(get_log_output(&work_dir), @r###"
356
356
@ kkmpptxzrspx false Add file4
357
357
โ rlvkpnrzqnoo false Add file3
358
-
โ qpvuntsmwlqt false Add file2
359
-
โ royxmykxtrkr false Add file1
358
+
โ royxmykxtrkr false Add file2
359
+
โ qpvuntsmwlqt false Add file1
360
360
โ zzzzzzzzzzzz true
361
361
[EOF]
362
-
");
362
+
"###);
363
363
364
364
// The commit we're splitting has a description, so the user will be
365
365
// prompted to enter a description for each of the commits.
···
388
388
// - The initial empty commit.
389
389
// - The rewritten commit from the snapshot after the files were added.
390
390
// - The rewritten commit once the description is added during `jj commit`.
391
-
// - The rewritten commit after the split with a new change ID.
392
-
let evolog_1 = work_dir.run_jj(["evolog", "-r", "royxm"]);
391
+
// - The rewritten commit after the split.
392
+
let evolog_1 = work_dir.run_jj(["evolog", "-r", "qpvun"]);
393
393
insta::assert_snapshot!(evolog_1, @r"
394
-
โ royxmykx test.user@example.com 2001-02-03 08:05:12 e13e94b9
394
+
โ qpvuntsm test.user@example.com 2001-02-03 08:05:12 74306e35
395
395
โ Add file1
396
-
โ -- operation a8006fdd66fd (2001-02-03 08:05:12) split commit 1d2499e72cefc8a2b87ebb47569140857b96189f
396
+
โ -- operation 994b490f285d (2001-02-03 08:05:12) split commit 1d2499e72cefc8a2b87ebb47569140857b96189f
397
397
โ qpvuntsm hidden test.user@example.com 2001-02-03 08:05:08 1d2499e7
398
398
โ Add file1 & file2
399
399
โ -- operation adf4f33386c9 (2001-02-03 08:05:08) commit f5700f8ef89e290e4e90ae6adc0908707e0d8c85
···
407
407
");
408
408
409
409
// The evolog for the second commit is the same, except that the change id
410
-
// doesn't change after the split.
411
-
let evolog_2 = work_dir.run_jj(["evolog", "-r", "qpvun"]);
410
+
// changes after the split.
411
+
let evolog_2 = work_dir.run_jj(["evolog", "-r", "royxm"]);
412
412
insta::assert_snapshot!(evolog_2, @r"
413
-
โ qpvuntsm test.user@example.com 2001-02-03 08:05:12 cf8ebbab
413
+
โ royxmykx test.user@example.com 2001-02-03 08:05:12 0a37745e
414
414
โ Add file2
415
-
โ -- operation a8006fdd66fd (2001-02-03 08:05:12) split commit 1d2499e72cefc8a2b87ebb47569140857b96189f
415
+
โ -- operation 994b490f285d (2001-02-03 08:05:12) split commit 1d2499e72cefc8a2b87ebb47569140857b96189f
416
416
โ qpvuntsm hidden test.user@example.com 2001-02-03 08:05:08 1d2499e7
417
417
โ Add file1 & file2
418
418
โ -- operation adf4f33386c9 (2001-02-03 08:05:08) commit f5700f8ef89e290e4e90ae6adc0908707e0d8c85
···
461
461
insta::assert_snapshot!(output, @r"
462
462
------- stderr -------
463
463
Rebased 1 descendant commits
464
-
Selected changes : royxmykx ad21dad2 Add file1
465
-
Remaining changes: kkmpptxz 0922bd25 Add file2
466
-
Working copy (@) now at: zsuskuln f59cd990 (empty) 2
464
+
Selected changes : kkmpptxz cc199567 Add file1
465
+
Remaining changes: royxmykx e488409f Add file2
466
+
Working copy (@) now at: zsuskuln ace61421 (empty) 2
467
467
Parent commit (@-) : qpvuntsm 884fe9b9 (empty) 1
468
-
Parent commit (@-) : kkmpptxz 0922bd25 Add file2
468
+
Parent commit (@-) : royxmykx e488409f Add file2
469
469
[EOF]
470
470
");
471
471
insta::assert_snapshot!(get_log_output(&work_dir), @r"
472
472
@ zsuskulnrvyr true 2
473
473
โโโฎ
474
-
โ โ kkmpptxzrspx false Add file2
475
-
โ โ royxmykxtrkr false Add file1
474
+
โ โ royxmykxtrkr false Add file2
475
+
โ โ kkmpptxzrspx false Add file1
476
476
โ โ qpvuntsmwlqt true 1
477
477
โโโฏ
478
478
โ zzzzzzzzzzzz true
···
498
498
โ zzzzzzzzzzzz true
499
499
[EOF]
500
500
------- stderr -------
501
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
501
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
502
502
[EOF]
503
503
"#);
504
504
···
510
510
let output = work_dir.run_jj(["split", "--parallel", "file1"]);
511
511
insta::assert_snapshot!(output, @r#"
512
512
------- stderr -------
513
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
514
-
Selected changes : kkmpptxz bd9b3db1 TESTED=TODO
515
-
Remaining changes: qpvuntsm 5597b805 (no description set)
516
-
Working copy (@) now at: qpvuntsm 5597b805 (no description set)
513
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
514
+
Selected changes : qpvuntsm 7bcd474c TESTED=TODO
515
+
Remaining changes: kkmpptxz 431886f6 (no description set)
516
+
Working copy (@) now at: kkmpptxz 431886f6 (no description set)
517
517
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
518
518
Added 0 files, modified 0 files, removed 1 files
519
519
[EOF]
520
520
"#);
521
521
insta::assert_snapshot!(get_log_output(&work_dir), @r#"
522
-
@ qpvuntsmwlqt false
523
-
โ โ kkmpptxzrspx false TESTED=TODO
522
+
@ kkmpptxzrspx false
523
+
โ โ qpvuntsmwlqt false TESTED=TODO
524
524
โโโฏ
525
525
โ zzzzzzzzzzzz true
526
526
[EOF]
527
527
------- stderr -------
528
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
528
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
529
529
[EOF]
530
530
"#);
531
531
···
550
550
// Check the evolog for the first commit. It shows three entries:
551
551
// - The initial empty commit.
552
552
// - The rewritten commit from the snapshot after the files were added.
553
-
// - The rewritten commit after the split with a new change ID.
554
-
let evolog_1 = work_dir.run_jj(["evolog", "-r", "kkmpp"]);
553
+
// - The rewritten commit after the split.
554
+
let evolog_1 = work_dir.run_jj(["evolog", "-r", "qpvun"]);
555
555
insta::assert_snapshot!(evolog_1, @r#"
556
-
โ kkmpptxz test.user@example.com 2001-02-03 08:05:09 bd9b3db1
556
+
โ qpvuntsm test.user@example.com 2001-02-03 08:05:09 7bcd474c
557
557
โ TESTED=TODO
558
-
โ -- operation 372a3799b434 (2001-02-03 08:05:09) split commit f5700f8ef89e290e4e90ae6adc0908707e0d8c85
558
+
โ -- operation 2b21c33e1596 (2001-02-03 08:05:09) split commit f5700f8ef89e290e4e90ae6adc0908707e0d8c85
559
559
โ qpvuntsm hidden test.user@example.com 2001-02-03 08:05:08 f5700f8e
560
560
โ (no description set)
561
561
โ -- operation 1663cd1cc445 (2001-02-03 08:05:08) snapshot working copy
···
564
564
-- operation 8f47435a3990 (2001-02-03 08:05:07) add workspace 'default'
565
565
[EOF]
566
566
------- stderr -------
567
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
567
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
568
568
[EOF]
569
569
"#);
570
570
571
571
// The evolog for the second commit is the same, except that the change id
572
-
// doesn't change after the split.
573
-
let evolog_2 = work_dir.run_jj(["evolog", "-r", "qpvun"]);
572
+
// changes after the split.
573
+
let evolog_2 = work_dir.run_jj(["evolog", "-r", "kkmpp"]);
574
574
insta::assert_snapshot!(evolog_2, @r#"
575
-
@ qpvuntsm test.user@example.com 2001-02-03 08:05:09 5597b805
575
+
@ kkmpptxz test.user@example.com 2001-02-03 08:05:09 431886f6
576
576
โ (no description set)
577
-
โ -- operation 372a3799b434 (2001-02-03 08:05:09) split commit f5700f8ef89e290e4e90ae6adc0908707e0d8c85
577
+
โ -- operation 2b21c33e1596 (2001-02-03 08:05:09) split commit f5700f8ef89e290e4e90ae6adc0908707e0d8c85
578
578
โ qpvuntsm hidden test.user@example.com 2001-02-03 08:05:08 f5700f8e
579
579
โ (no description set)
580
580
โ -- operation 1663cd1cc445 (2001-02-03 08:05:08) snapshot working copy
···
583
583
-- operation 8f47435a3990 (2001-02-03 08:05:07) add workspace 'default'
584
584
[EOF]
585
585
------- stderr -------
586
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
586
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
587
587
[EOF]
588
588
"#);
589
589
}
···
637
637
insta::assert_snapshot!(output, @r"
638
638
------- stderr -------
639
639
Rebased 2 descendant commits
640
-
Selected changes : vruxwmqv 3f0980cb Add file1
641
-
Remaining changes: qpvuntsm dff79d19 Add file2
642
-
Working copy (@) now at: qpvuntsm dff79d19 Add file2
640
+
Selected changes : qpvuntsm 18c85f56 Add file1
641
+
Remaining changes: vruxwmqv cbdfd9cf Add file2
642
+
Working copy (@) now at: vruxwmqv cbdfd9cf Add file2
643
643
Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set)
644
644
Added 0 files, modified 0 files, removed 1 files
645
645
[EOF]
···
648
648
โ kkmpptxzrspx false Add file4
649
649
โ rlvkpnrzqnoo false Add file3
650
650
โโโฎ
651
-
โ @ qpvuntsmwlqt false Add file2
652
-
โ โ vruxwmqvtpmx false Add file1
651
+
โ @ vruxwmqvtpmx false Add file2
652
+
โ โ qpvuntsmwlqt false Add file1
653
653
โโโฏ
654
654
โ zzzzzzzzzzzz true
655
655
[EOF]
···
714
714
insta::assert_snapshot!(output, @r"
715
715
------- stderr -------
716
716
Rebased 1 descendant commits
717
-
Selected changes : royxmykx ad21dad2 Add file1
718
-
Remaining changes: kkmpptxz 23a2daac Add file2
719
-
Working copy (@) now at: zsuskuln f1fcb7a6 (empty) 2
717
+
Selected changes : kkmpptxz cc199567 Add file1
718
+
Remaining changes: royxmykx 82a5c527 Add file2
719
+
Working copy (@) now at: zsuskuln b7cdcdec (empty) 2
720
720
Parent commit (@-) : qpvuntsm 884fe9b9 (empty) 1
721
-
Parent commit (@-) : royxmykx ad21dad2 Add file1
722
-
Parent commit (@-) : kkmpptxz 23a2daac Add file2
721
+
Parent commit (@-) : kkmpptxz cc199567 Add file1
722
+
Parent commit (@-) : royxmykx 82a5c527 Add file2
723
723
[EOF]
724
724
");
725
725
insta::assert_snapshot!(get_log_output(&work_dir), @r"
726
726
@ zsuskulnrvyr true 2
727
727
โโโฌโโฎ
728
-
โ โ โ kkmpptxzrspx false Add file2
729
-
โ โ โ royxmykxtrkr false Add file1
728
+
โ โ โ royxmykxtrkr false Add file2
729
+
โ โ โ kkmpptxzrspx false Add file1
730
730
โ โโโฏ
731
731
โ โ qpvuntsmwlqt true 1
732
732
โโโฏ
···
793
793
let output = work_dir.run_jj(["split"]);
794
794
insta::assert_snapshot!(output, @r"
795
795
------- stderr -------
796
-
Selected changes : rlvkpnrz 1ff7a783 (no description set)
797
-
Remaining changes: qpvuntsm 429f292f (no description set)
798
-
Working copy (@) now at: qpvuntsm 429f292f (no description set)
799
-
Parent commit (@-) : rlvkpnrz 1ff7a783 (no description set)
796
+
Selected changes : qpvuntsm c664a51b (no description set)
797
+
Remaining changes: rlvkpnrz 7e5d65b1 (no description set)
798
+
Working copy (@) now at: rlvkpnrz 7e5d65b1 (no description set)
799
+
Parent commit (@-) : qpvuntsm c664a51b (no description set)
800
800
[EOF]
801
801
");
802
802
···
824
824
825
825
let output = work_dir.run_jj(["log", "--summary"]);
826
826
insta::assert_snapshot!(output, @r"
827
-
@ qpvuntsm test.user@example.com 2001-02-03 08:05:08 429f292f
827
+
@ rlvkpnrz test.user@example.com 2001-02-03 08:05:08 7e5d65b1
828
828
โ (no description set)
829
829
โ A file2
830
-
โ rlvkpnrz test.user@example.com 2001-02-03 08:05:08 1ff7a783
830
+
โ qpvuntsm test.user@example.com 2001-02-03 08:05:08 c664a51b
831
831
โ (no description set)
832
832
โ A file1
833
833
โ zzzzzzzz root() 00000000
···
869
869
let output = work_dir.run_jj(["split", "-i", "file1", "file2"]);
870
870
insta::assert_snapshot!(output, @r"
871
871
------- stderr -------
872
-
Selected changes : kkmpptxz 0a5bea34 (no description set)
873
-
Remaining changes: rlvkpnrz 7326e6fd (no description set)
874
-
Working copy (@) now at: rlvkpnrz 7326e6fd (no description set)
875
-
Parent commit (@-) : kkmpptxz 0a5bea34 (no description set)
872
+
Selected changes : rlvkpnrz cdc9960a (no description set)
873
+
Remaining changes: kkmpptxz 7255f070 (no description set)
874
+
Working copy (@) now at: kkmpptxz 7255f070 (no description set)
875
+
Parent commit (@-) : rlvkpnrz cdc9960a (no description set)
876
876
[EOF]
877
877
");
878
878
···
889
889
890
890
let output = work_dir.run_jj(["log", "--summary"]);
891
891
insta::assert_snapshot!(output, @r"
892
-
@ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 7326e6fd
892
+
@ kkmpptxz test.user@example.com 2001-02-03 08:05:09 7255f070
893
893
โ (no description set)
894
894
โ M file2
895
895
โ M file3
896
-
โ kkmpptxz test.user@example.com 2001-02-03 08:05:09 0a5bea34
896
+
โ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 cdc9960a
897
897
โ (no description set)
898
898
โ A file1
899
899
โ qpvuntsm test.user@example.com 2001-02-03 08:05:08 ff687a2f
···
947
947
main_dir.run_jj(["split", "file2"]).success();
948
948
// The working copy for both workspaces will be the second split commit.
949
949
insta::assert_snapshot!(get_workspace_log_output(&main_dir), @r"
950
-
@ qpvuntsmwlqt default@ second@ second-commit
951
-
โ royxmykxtrkr first-commit
950
+
@ royxmykxtrkr default@ second@ second-commit
951
+
โ qpvuntsmwlqt first-commit
952
952
โ zzzzzzzzzzzz
953
953
[EOF]
954
954
");
···
962
962
.unwrap();
963
963
main_dir.run_jj(["split", "file2", "--parallel"]).success();
964
964
insta::assert_snapshot!(get_workspace_log_output(&main_dir), @r"
965
-
@ qpvuntsmwlqt default@ second@ second-commit
966
-
โ โ yostqsxwqrlt first-commit
965
+
@ yostqsxwqrlt default@ second@ second-commit
966
+
โ โ qpvuntsmwlqt first-commit
967
967
โโโฏ
968
968
โ zzzzzzzzzzzz
969
969
[EOF]
···
1006
1006
main_dir.run_jj(["split", "file2"]).success();
1007
1007
// Only the working copy commit for the default workspace changes.
1008
1008
insta::assert_snapshot!(get_workspace_log_output(&main_dir), @r"
1009
-
@ qpvuntsmwlqt default@ second-commit
1010
-
โ mzvwutvlkqwt first-commit
1009
+
@ mzvwutvlkqwt default@ second-commit
1010
+
โ qpvuntsmwlqt first-commit
1011
1011
โ โ pmmvwywvzvvn second@
1012
1012
โโโฏ
1013
1013
โ zzzzzzzzzzzz
···
1023
1023
.unwrap();
1024
1024
main_dir.run_jj(["split", "file2", "--parallel"]).success();
1025
1025
insta::assert_snapshot!(get_workspace_log_output(&main_dir), @r"
1026
-
@ qpvuntsmwlqt default@ second-commit
1027
-
โ โ vruxwmqvtpmx first-commit
1026
+
@ vruxwmqvtpmx default@ second-commit
1027
+
โ โ qpvuntsmwlqt first-commit
1028
1028
โโโฏ
1029
1029
โ โ pmmvwywvzvvn second@
1030
1030
โโโฏ
···
1064
1064
let output = work_dir.run_jj(["split", "file1"]);
1065
1065
insta::assert_snapshot!(output, @r#"
1066
1066
------- stderr -------
1067
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
1068
-
Selected changes : kkmpptxz 530f78ed part 1
1069
-
Remaining changes: qpvuntsm 88189e08 part 2
1070
-
Working copy (@) now at: qpvuntsm 88189e08 part 2
1071
-
Parent commit (@-) : kkmpptxz 530f78ed part 1
1067
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
1068
+
Selected changes : qpvuntsm c7f7b14b part 1
1069
+
Remaining changes: kkmpptxz ac33a5a9 part 2
1070
+
Working copy (@) now at: kkmpptxz ac33a5a9 part 2
1071
+
Parent commit (@-) : qpvuntsm c7f7b14b part 1
1072
1072
[EOF]
1073
1073
"#);
1074
1074
···
1097
1097
JJ: Lines starting with "JJ:" (like this one) will be removed.
1098
1098
"#);
1099
1099
insta::assert_snapshot!(get_log_output(&work_dir), @r#"
1100
-
@ qpvuntsmwlqt false part 2
1101
-
โ kkmpptxzrspx false part 1
1100
+
@ kkmpptxzrspx false part 2
1101
+
โ qpvuntsmwlqt false part 1
1102
1102
โ zzzzzzzzzzzz true
1103
1103
[EOF]
1104
1104
------- stderr -------
1105
-
Warning: Deprecated config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
1105
+
Warning: Deprecated user-level config: ui.default-description is updated to template-aliases.default_commit_description = '"\n\nTESTED=TODO\n"'
1106
1106
[EOF]
1107
1107
"#);
1108
1108
}
···
1120
1120
let output = work_dir.run_jj(["split", "-m", "fix in file1", "file1"]);
1121
1121
insta::assert_snapshot!(output, @r"
1122
1122
------- stderr -------
1123
-
Selected changes : kkmpptxz b246503a fix in file1
1124
-
Remaining changes: qpvuntsm e05b5012 my feature
1125
-
Working copy (@) now at: qpvuntsm e05b5012 my feature
1126
-
Parent commit (@-) : kkmpptxz b246503a fix in file1
1123
+
Selected changes : qpvuntsm f2a70519 fix in file1
1124
+
Remaining changes: kkmpptxz cac11766 my feature
1125
+
Working copy (@) now at: kkmpptxz cac11766 my feature
1126
+
Parent commit (@-) : qpvuntsm f2a70519 fix in file1
1127
1127
[EOF]
1128
1128
");
1129
1129
1130
1130
insta::assert_snapshot!(get_log_output(&work_dir), @r"
1131
-
@ qpvuntsmwlqt false my feature
1132
-
โ kkmpptxzrspx false fix in file1
1131
+
@ kkmpptxzrspx false my feature
1132
+
โ qpvuntsmwlqt false fix in file1
1133
1133
โ zzzzzzzzzzzz true
1134
1134
[EOF]
1135
1135
");
···
1146
1146
]);
1147
1147
insta::assert_snapshot!(output, @r"
1148
1148
------- stderr -------
1149
-
Selected changes : royxmykx 87fbb488 fix in file1
1150
-
Remaining changes: qpvuntsm fb598346 my feature
1151
-
Working copy (@) now at: qpvuntsm fb598346 my feature
1152
-
Parent commit (@-) : royxmykx 87fbb488 fix in file1
1149
+
Selected changes : qpvuntsm d01cf12d fix in file1
1150
+
Remaining changes: royxmykx b1556ed9 my feature
1151
+
Working copy (@) now at: royxmykx b1556ed9 my feature
1152
+
Parent commit (@-) : qpvuntsm d01cf12d fix in file1
1153
1153
[EOF]
1154
1154
");
1155
1155
1156
1156
insta::assert_snapshot!(get_log_output(&work_dir), @r"
1157
-
@ qpvuntsmwlqt false my feature
1158
-
โ royxmykxtrkr false fix in file1
1157
+
@ royxmykxtrkr false my feature
1158
+
โ qpvuntsmwlqt false fix in file1
1159
1159
โ
1160
1160
โ CC: test.user@example.com
1161
1161
โ zzzzzzzzzzzz true
···
1470
1470
.unwrap();
1471
1471
let output = main_dir.run_jj(["split", "file2"]);
1472
1472
match bookmark_behavior {
1473
-
BookmarkBehavior::Default | BookmarkBehavior::LeaveBookmarkWithTarget => {
1473
+
BookmarkBehavior::LeaveBookmarkWithTarget => {
1474
1474
insta::allow_duplicates! {
1475
1475
insta::assert_snapshot!(output, @r#"
1476
1476
------- stderr -------
1477
-
Selected changes : mzvwutvl ac5cf500 first-commit
1478
-
Remaining changes: qpvuntsm a13c536a "*le-signet*" | second-commit
1479
-
Working copy (@) now at: qpvuntsm a13c536a "*le-signet*" | second-commit
1480
-
Parent commit (@-) : mzvwutvl ac5cf500 first-commit
1477
+
Selected changes : qpvuntsm a481fe8a "*le-signet*" | first-commit
1478
+
Remaining changes: mzvwutvl 5f597a6e second-commit
1479
+
Working copy (@) now at: mzvwutvl 5f597a6e second-commit
1480
+
Parent commit (@-) : qpvuntsm a481fe8a "*le-signet*" | first-commit
1481
1481
[EOF]
1482
1482
"#);
1483
1483
}
1484
1484
insta::allow_duplicates! {
1485
1485
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
1486
-
@ qpvuntsmwlqt false "*le-signet*" second-commit
1487
-
โ mzvwutvlkqwt false first-commit
1486
+
@ mzvwutvlkqwt false second-commit
1487
+
โ qpvuntsmwlqt false "*le-signet*" first-commit
1488
1488
โ zzzzzzzzzzzz true
1489
1489
[EOF]
1490
1490
"#);
1491
1491
}
1492
1492
}
1493
-
BookmarkBehavior::MoveBookmarkToChild => {
1493
+
BookmarkBehavior::Default | BookmarkBehavior::MoveBookmarkToChild => {
1494
1494
insta::allow_duplicates! {
1495
1495
insta::assert_snapshot!(output, @r#"
1496
1496
------- stderr -------
···
1521
1521
.unwrap();
1522
1522
main_dir.run_jj(["split", "file2", "--parallel"]).success();
1523
1523
match bookmark_behavior {
1524
-
BookmarkBehavior::Default | BookmarkBehavior::LeaveBookmarkWithTarget => {
1524
+
BookmarkBehavior::LeaveBookmarkWithTarget => {
1525
1525
insta::allow_duplicates! {
1526
1526
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
1527
-
@ qpvuntsmwlqt false "*le-signet*" second-commit
1528
-
โ โ vruxwmqvtpmx false first-commit
1527
+
@ vruxwmqvtpmx false second-commit
1528
+
โ โ qpvuntsmwlqt false "*le-signet*" first-commit
1529
1529
โโโฏ
1530
1530
โ zzzzzzzzzzzz true
1531
1531
[EOF]
1532
1532
"#);
1533
1533
}
1534
1534
}
1535
-
BookmarkBehavior::MoveBookmarkToChild => {
1535
+
BookmarkBehavior::Default | BookmarkBehavior::MoveBookmarkToChild => {
1536
1536
insta::allow_duplicates! {
1537
1537
insta::assert_snapshot!(get_log_output(&main_dir), @r#"
1538
1538
@ vruxwmqvtpmx false "*le-signet*" second-commit
-775
cli/tests/test_squash_command.rs
-775
cli/tests/test_squash_command.rs
···
746
746
}
747
747
748
748
#[test]
749
-
fn test_squash_working_copy_restore_descendants() {
750
-
let test_env = TestEnvironment::default();
751
-
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
752
-
let work_dir = test_env.work_dir("repo");
753
-
754
-
// Create history like this:
755
-
// Y
756
-
// |
757
-
// B X@
758
-
// |/
759
-
// A
760
-
//
761
-
// Each commit adds a file named the same as the commit
762
-
let create_commit = |name: &str| {
763
-
work_dir
764
-
.run_jj(["bookmark", "create", "-r@", name])
765
-
.success();
766
-
work_dir.write_file(name, format!("test {name}\n"));
767
-
};
768
-
769
-
create_commit("a");
770
-
work_dir.run_jj(["new"]).success();
771
-
create_commit("b");
772
-
work_dir.run_jj(["new", "a"]).success();
773
-
create_commit("x");
774
-
work_dir.run_jj(["new"]).success();
775
-
create_commit("y");
776
-
work_dir.run_jj(["edit", "x"]).success();
777
-
778
-
let template = r#"separate(
779
-
" ",
780
-
commit_id.short(),
781
-
bookmarks,
782
-
description,
783
-
if(empty, "(empty)")
784
-
)"#;
785
-
let run_log = || work_dir.run_jj(["log", "-r=::", "--summary", "-T", template]);
786
-
787
-
// Verify the setup
788
-
insta::assert_snapshot!(run_log(), @r"
789
-
โ 3f45d7a3ae69 y
790
-
โ A y
791
-
@ 5b4046443e64 x
792
-
โ A x
793
-
โ โ b1e1eea2f666 b
794
-
โโโฏ A b
795
-
โ 7468364c89fc a
796
-
โ A a
797
-
โ 000000000000 (empty)
798
-
[EOF]
799
-
");
800
-
let output = work_dir.run_jj(["file", "list", "-r=a"]);
801
-
insta::assert_snapshot!(output, @r"
802
-
a
803
-
[EOF]
804
-
");
805
-
let output = work_dir.run_jj(["file", "list", "-r=b"]);
806
-
insta::assert_snapshot!(output, @r"
807
-
a
808
-
b
809
-
[EOF]
810
-
");
811
-
let output = work_dir.run_jj(["file", "list"]);
812
-
insta::assert_snapshot!(output, @r"
813
-
a
814
-
x
815
-
[EOF]
816
-
");
817
-
let output = work_dir.run_jj(["file", "list", "-r=y"]);
818
-
insta::assert_snapshot!(output, @r"
819
-
a
820
-
x
821
-
y
822
-
[EOF]
823
-
");
824
-
825
-
let output = work_dir.run_jj(["squash", "--restore-descendants"]);
826
-
insta::assert_snapshot!(output, @r"
827
-
------- stderr -------
828
-
Rebased 2 descendant commits (while preserving their content)
829
-
Working copy (@) now at: kxryzmor 7ec5499d (empty) (no description set)
830
-
Parent commit (@-) : qpvuntsm 1c6a069e a x | (no description set)
831
-
[EOF]
832
-
");
833
-
insta::assert_snapshot!(run_log(), @r"
834
-
@ 7ec5499d9141 (empty)
835
-
โ โ ddfef0b279f8 y
836
-
โโโฏ A y
837
-
โ โ 640ba5e85507 b
838
-
โโโฏ A b
839
-
โ D x
840
-
โ 1c6a069ec7e3 a x
841
-
โ A a
842
-
โ A x
843
-
โ 000000000000 (empty)
844
-
[EOF]
845
-
");
846
-
847
-
let output = work_dir.run_jj(["diff", "--summary"]);
848
-
// The current commit becomes empty.
849
-
insta::assert_snapshot!(output, @"");
850
-
// Should coincide with the working copy commit before
851
-
let output = work_dir.run_jj(["file", "list", "-r=a"]);
852
-
insta::assert_snapshot!(output, @r"
853
-
a
854
-
x
855
-
[EOF]
856
-
");
857
-
// Commit b should be the same as before
858
-
let output = work_dir.run_jj(["file", "list", "-r=b"]);
859
-
insta::assert_snapshot!(output, @r"
860
-
a
861
-
b
862
-
[EOF]
863
-
");
864
-
let output = work_dir.run_jj(["file", "list", "-r=y"]);
865
-
insta::assert_snapshot!(output, @r"
866
-
a
867
-
x
868
-
y
869
-
[EOF]
870
-
");
871
-
}
872
-
873
-
#[test]
874
-
fn test_squash_from_to_restore_descendants() {
875
-
let test_env = TestEnvironment::default();
876
-
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
877
-
let work_dir = test_env.work_dir("repo");
878
-
879
-
// Create history like this:
880
-
// F
881
-
// |\
882
-
// E C
883
-
// | |
884
-
// D B
885
-
// |/
886
-
// A
887
-
//
888
-
// Each commit adds a file named the same as the commit
889
-
let create_commit = |name: &str| {
890
-
work_dir
891
-
.run_jj(["bookmark", "create", "-r@", name])
892
-
.success();
893
-
work_dir.write_file(name, format!("test {name}\n"));
894
-
};
895
-
896
-
create_commit("a");
897
-
work_dir.run_jj(["new"]).success();
898
-
create_commit("b");
899
-
work_dir.run_jj(["new"]).success();
900
-
create_commit("c");
901
-
work_dir.run_jj(["new", "a"]).success();
902
-
create_commit("d");
903
-
work_dir.run_jj(["new"]).success();
904
-
create_commit("e");
905
-
work_dir.run_jj(["new", "e", "c"]).success();
906
-
create_commit("f");
907
-
908
-
let template = r#"separate(
909
-
" ",
910
-
commit_id.short(),
911
-
bookmarks,
912
-
description,
913
-
if(empty, "(empty)")
914
-
)"#;
915
-
let run_log = || work_dir.run_jj(["log", "-r=::", "--summary", "-T", template]);
916
-
917
-
// ========== Part 1 =========
918
-
// Verify the setup
919
-
insta::assert_snapshot!(run_log(), @r"
920
-
@ 42acd0537c88 f
921
-
โโโฎ A f
922
-
โ โ 4fb9706b0f47 c
923
-
โ โ A c
924
-
โ โ b1e1eea2f666 b
925
-
โ โ A b
926
-
โ โ b4e3197108ba e
927
-
โ โ A e
928
-
โ โ d707102f499f d
929
-
โโโฏ A d
930
-
โ 7468364c89fc a
931
-
โ A a
932
-
โ 000000000000 (empty)
933
-
[EOF]
934
-
");
935
-
let beginning = work_dir.current_operation_id();
936
-
test_env.advance_test_rng_seed_to_multiple_of(200_000);
937
-
938
-
// Squash without --restore-descendants for comparison
939
-
work_dir
940
-
.run_jj(["operation", "restore", &beginning])
941
-
.success();
942
-
let output = work_dir.run_jj(["squash", "--from=b", "--into=d"]);
943
-
insta::assert_snapshot!(output, @r"
944
-
------- stderr -------
945
-
Rebased 3 descendant commits
946
-
Working copy (@) now at: kpqxywon e462100a f | (no description set)
947
-
Parent commit (@-) : yostqsxw 6944fd03 e | (no description set)
948
-
Parent commit (@-) : mzvwutvl 6cd5d5c1 c | (no description set)
949
-
[EOF]
950
-
");
951
-
insta::assert_snapshot!(run_log(), @r"
952
-
@ e462100ae7c3 f
953
-
โโโฎ A f
954
-
โ โ 6cd5d5c1daf7 c
955
-
โ โ A c
956
-
โ โ 6944fd03dc5d e
957
-
โ โ A e
958
-
โ โ 1befcf027d1b d
959
-
โโโฏ A b
960
-
โ A d
961
-
โ 7468364c89fc a b
962
-
โ A a
963
-
โ 000000000000 (empty)
964
-
[EOF]
965
-
");
966
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
967
-
insta::assert_snapshot!(output, @r"
968
-
a
969
-
b
970
-
d
971
-
[EOF]
972
-
");
973
-
let output = work_dir.run_jj(["file", "list", "-r=c"]);
974
-
insta::assert_snapshot!(output, @r"
975
-
a
976
-
c
977
-
[EOF]
978
-
");
979
-
let output = work_dir.run_jj(["file", "list", "-r=e"]);
980
-
insta::assert_snapshot!(output, @r"
981
-
a
982
-
b
983
-
d
984
-
e
985
-
[EOF]
986
-
");
987
-
let output = work_dir.run_jj(["file", "list", "-r=f"]);
988
-
insta::assert_snapshot!(output, @r"
989
-
a
990
-
b
991
-
c
992
-
d
993
-
e
994
-
f
995
-
[EOF]
996
-
");
997
-
998
-
// --restore-descendants
999
-
work_dir
1000
-
.run_jj(["operation", "restore", &beginning])
1001
-
.success();
1002
-
let output = work_dir.run_jj(["squash", "--from=b", "--into=d", "--restore-descendants"]);
1003
-
insta::assert_snapshot!(output, @r"
1004
-
------- stderr -------
1005
-
Rebased 3 descendant commits (while preserving their content)
1006
-
Working copy (@) now at: kpqxywon 1d64ccbf f | (no description set)
1007
-
Parent commit (@-) : yostqsxw cb90d752 e | (no description set)
1008
-
Parent commit (@-) : mzvwutvl 4e6702ae c | (no description set)
1009
-
[EOF]
1010
-
");
1011
-
// `d`` becomes the same as in the above example,
1012
-
// but `c` does not lose file `b` and `e` still does not contain file `b`
1013
-
// regardless of what happened to their parents.
1014
-
insta::assert_snapshot!(run_log(), @r"
1015
-
@ 1d64ccbf4608 f
1016
-
โโโฎ A f
1017
-
โ โ 4e6702ae494c c
1018
-
โ โ A b
1019
-
โ โ A c
1020
-
โ โ cb90d75271b4 e
1021
-
โ โ D b
1022
-
โ โ A e
1023
-
โ โ 853ea07451aa d
1024
-
โโโฏ A b
1025
-
โ A d
1026
-
โ 7468364c89fc a b
1027
-
โ A a
1028
-
โ 000000000000 (empty)
1029
-
[EOF]
1030
-
");
1031
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1032
-
insta::assert_snapshot!(output, @r"
1033
-
a
1034
-
b
1035
-
d
1036
-
[EOF]
1037
-
");
1038
-
let output = work_dir.run_jj(["file", "list", "-r=c"]);
1039
-
insta::assert_snapshot!(output, @r"
1040
-
a
1041
-
b
1042
-
c
1043
-
[EOF]
1044
-
");
1045
-
let output = work_dir.run_jj(["file", "list", "-r=e"]);
1046
-
insta::assert_snapshot!(output, @r"
1047
-
a
1048
-
d
1049
-
e
1050
-
[EOF]
1051
-
");
1052
-
let output = work_dir.run_jj(["file", "list", "-r=f"]);
1053
-
insta::assert_snapshot!(output, @r"
1054
-
a
1055
-
b
1056
-
c
1057
-
d
1058
-
e
1059
-
f
1060
-
[EOF]
1061
-
");
1062
-
1063
-
// --restore-descendants works with --keep-emptied, same result except for
1064
-
// leaving an empty commit
1065
-
work_dir
1066
-
.run_jj(["operation", "restore", &beginning])
1067
-
.success();
1068
-
let output = work_dir.run_jj([
1069
-
"squash",
1070
-
"--from=b",
1071
-
"--into=d",
1072
-
"--restore-descendants",
1073
-
"--keep-emptied",
1074
-
]);
1075
-
insta::assert_snapshot!(output, @r"
1076
-
------- stderr -------
1077
-
Rebased 3 descendant commits (while preserving their content)
1078
-
Working copy (@) now at: kpqxywon 3c13920f f | (no description set)
1079
-
Parent commit (@-) : yostqsxw aa73012d e | (no description set)
1080
-
Parent commit (@-) : mzvwutvl d323deaa c | (no description set)
1081
-
[EOF]
1082
-
");
1083
-
// `d`` becomes the same as in the above example,
1084
-
// but `c` does not lose file `b` and `e` still does not contain file `b`
1085
-
// regardless of what happened to their parents.
1086
-
insta::assert_snapshot!(run_log(), @r"
1087
-
@ 3c13920f1e9a f
1088
-
โโโฎ A f
1089
-
โ โ d323deaa04c2 c
1090
-
โ โ A b
1091
-
โ โ A c
1092
-
โ โ a55451e8808f b (empty)
1093
-
โ โ aa73012df9cd e
1094
-
โ โ D b
1095
-
โ โ A e
1096
-
โ โ d00e73142243 d
1097
-
โโโฏ A b
1098
-
โ A d
1099
-
โ 7468364c89fc a
1100
-
โ A a
1101
-
โ 000000000000 (empty)
1102
-
[EOF]
1103
-
");
1104
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1105
-
insta::assert_snapshot!(output, @r"
1106
-
a
1107
-
b
1108
-
d
1109
-
[EOF]
1110
-
");
1111
-
let output = work_dir.run_jj(["file", "list", "-r=c"]);
1112
-
insta::assert_snapshot!(output, @r"
1113
-
a
1114
-
b
1115
-
c
1116
-
[EOF]
1117
-
");
1118
-
let output = work_dir.run_jj(["file", "list", "-r=e"]);
1119
-
insta::assert_snapshot!(output, @r"
1120
-
a
1121
-
d
1122
-
e
1123
-
[EOF]
1124
-
");
1125
-
1126
-
// ========== Part 2 =========
1127
-
// Reminder of the setup
1128
-
test_env.advance_test_rng_seed_to_multiple_of(200_000);
1129
-
work_dir
1130
-
.run_jj(["operation", "restore", &beginning])
1131
-
.success();
1132
-
insta::assert_snapshot!(run_log(), @r"
1133
-
@ 42acd0537c88 f
1134
-
โโโฎ A f
1135
-
โ โ 4fb9706b0f47 c
1136
-
โ โ A c
1137
-
โ โ b1e1eea2f666 b
1138
-
โ โ A b
1139
-
โ โ b4e3197108ba e
1140
-
โ โ A e
1141
-
โ โ d707102f499f d
1142
-
โโโฏ A d
1143
-
โ 7468364c89fc a
1144
-
โ A a
1145
-
โ 000000000000 (empty)
1146
-
[EOF]
1147
-
");
1148
-
let output = work_dir.run_jj(["file", "list", "-r=c"]);
1149
-
insta::assert_snapshot!(output, @r"
1150
-
a
1151
-
b
1152
-
c
1153
-
[EOF]
1154
-
");
1155
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1156
-
insta::assert_snapshot!(output, @r"
1157
-
a
1158
-
d
1159
-
[EOF]
1160
-
");
1161
-
1162
-
// --restore-descendants works when squashing from parent to child
1163
-
work_dir
1164
-
.run_jj(["operation", "restore", &beginning])
1165
-
.success();
1166
-
let output = work_dir.run_jj(["squash", "--from=a", "--into=b", "--restore-descendants"]);
1167
-
insta::assert_snapshot!(output, @r"
1168
-
------- stderr -------
1169
-
Rebased 2 descendant commits (while preserving their content)
1170
-
Working copy (@) now at: kpqxywon 7fa445c9 f | (no description set)
1171
-
Parent commit (@-) : yostqsxw 102e6106 e | (no description set)
1172
-
Parent commit (@-) : mzvwutvl a2ff7c27 c | (no description set)
1173
-
[EOF]
1174
-
");
1175
-
insta::assert_snapshot!(run_log(), @r"
1176
-
@ 7fa445c9e606 f
1177
-
โโโฎ A f
1178
-
โ โ a2ff7c27dbba c
1179
-
โ โ A c
1180
-
โ โ 2bf81678391c b
1181
-
โ โ A a
1182
-
โ โ A b
1183
-
โ โ 102e61065eb2 e
1184
-
โ โ A e
1185
-
โ โ 7b1493a2027e d
1186
-
โโโฏ A a
1187
-
โ A d
1188
-
โ 000000000000 a (empty)
1189
-
[EOF]
1190
-
");
1191
-
let output = work_dir.run_jj(["file", "list", "-r=b"]);
1192
-
insta::assert_snapshot!(output, @r"
1193
-
a
1194
-
b
1195
-
[EOF]
1196
-
");
1197
-
let output = work_dir.run_jj(["file", "list", "-r=c"]);
1198
-
insta::assert_snapshot!(output, @r"
1199
-
a
1200
-
b
1201
-
c
1202
-
[EOF]
1203
-
");
1204
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1205
-
insta::assert_snapshot!(output, @r"
1206
-
a
1207
-
d
1208
-
[EOF]
1209
-
");
1210
-
1211
-
// --restore-descendants --keep-emptied works when squashing from parent to
1212
-
// child
1213
-
work_dir
1214
-
.run_jj(["operation", "restore", &beginning])
1215
-
.success();
1216
-
let output = work_dir.run_jj([
1217
-
"squash",
1218
-
"--from=a",
1219
-
"--into=b",
1220
-
"--restore-descendants",
1221
-
"--keep-emptied",
1222
-
]);
1223
-
insta::assert_snapshot!(output, @r"
1224
-
------- stderr -------
1225
-
Rebased 2 descendant commits (while preserving their content)
1226
-
Working copy (@) now at: kpqxywon 30c1ec1b f | (no description set)
1227
-
Parent commit (@-) : yostqsxw c20a2a7a e | (no description set)
1228
-
Parent commit (@-) : mzvwutvl 601223f5 c | (no description set)
1229
-
[EOF]
1230
-
");
1231
-
insta::assert_snapshot!(run_log(), @r"
1232
-
@ 30c1ec1b6264 f
1233
-
โโโฎ A f
1234
-
โ โ 601223f5faa8 c
1235
-
โ โ A c
1236
-
โ โ 28223a4af36c b
1237
-
โ โ A a
1238
-
โ โ A b
1239
-
โ โ c20a2a7a24ba e
1240
-
โ โ A e
1241
-
โ โ a224ba6ebde8 d
1242
-
โโโฏ A a
1243
-
โ A d
1244
-
โ 367fe826e43e a (empty)
1245
-
โ 000000000000 (empty)
1246
-
[EOF]
1247
-
");
1248
-
let output = work_dir.run_jj(["file", "list", "-r=b"]);
1249
-
insta::assert_snapshot!(output, @r"
1250
-
a
1251
-
b
1252
-
[EOF]
1253
-
");
1254
-
let output = work_dir.run_jj(["file", "list", "-r=c"]);
1255
-
insta::assert_snapshot!(output, @r"
1256
-
a
1257
-
b
1258
-
c
1259
-
[EOF]
1260
-
");
1261
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1262
-
insta::assert_snapshot!(output, @r"
1263
-
a
1264
-
d
1265
-
[EOF]
1266
-
");
1267
-
1268
-
// --restore-descendants works when squashing from child to parent
1269
-
work_dir
1270
-
.run_jj(["operation", "restore", &beginning])
1271
-
.success();
1272
-
let output = work_dir.run_jj(["squash", "--from=b", "--into=a", "--restore-descendants"]);
1273
-
insta::assert_snapshot!(output, @r"
1274
-
------- stderr -------
1275
-
Rebased 4 descendant commits (while preserving their content)
1276
-
Working copy (@) now at: kpqxywon 6ad1c62a f | (no description set)
1277
-
Parent commit (@-) : yostqsxw e259f026 e | (no description set)
1278
-
Parent commit (@-) : mzvwutvl 36192c59 c | (no description set)
1279
-
[EOF]
1280
-
");
1281
-
insta::assert_snapshot!(run_log(), @r"
1282
-
@ 6ad1c62aec5b f
1283
-
โโโฎ A b
1284
-
โ โ A f
1285
-
โ โ 36192c59f1e9 c
1286
-
โ โ A c
1287
-
โ โ e259f02633ca e
1288
-
โ โ A e
1289
-
โ โ 92943f1c8204 d
1290
-
โโโฏ D b
1291
-
โ A d
1292
-
โ 59aac8514774 a b
1293
-
โ A a
1294
-
โ A b
1295
-
โ 000000000000 (empty)
1296
-
[EOF]
1297
-
");
1298
-
let output = work_dir.run_jj(["file", "list", "-r=b"]);
1299
-
insta::assert_snapshot!(output, @r"
1300
-
a
1301
-
b
1302
-
[EOF]
1303
-
");
1304
-
let output = work_dir.run_jj(["file", "list", "-r=c"]);
1305
-
insta::assert_snapshot!(output, @r"
1306
-
a
1307
-
b
1308
-
c
1309
-
[EOF]
1310
-
");
1311
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1312
-
insta::assert_snapshot!(output, @r"
1313
-
a
1314
-
d
1315
-
[EOF]
1316
-
");
1317
-
1318
-
// same test, but with --keep-emptied
1319
-
work_dir
1320
-
.run_jj(["operation", "restore", &beginning])
1321
-
.success();
1322
-
let output = work_dir.run_jj([
1323
-
"squash",
1324
-
"--from=b",
1325
-
"--into=a",
1326
-
"--keep-emptied",
1327
-
"--restore-descendants",
1328
-
]);
1329
-
insta::assert_snapshot!(output, @r"
1330
-
------- stderr -------
1331
-
Rebased 5 descendant commits (while preserving their content)
1332
-
Working copy (@) now at: kpqxywon 6eadede0 f | (no description set)
1333
-
Parent commit (@-) : yostqsxw 97233b50 e | (no description set)
1334
-
Parent commit (@-) : mzvwutvl 5b2d6858 c | (no description set)
1335
-
[EOF]
1336
-
");
1337
-
// BUG! b should now be empty!
1338
-
insta::assert_snapshot!(run_log(), @r"
1339
-
@ 6eadede086b1 f
1340
-
โโโฎ A b
1341
-
โ โ A f
1342
-
โ โ 5b2d685868b7 c
1343
-
โ โ A b
1344
-
โ โ A c
1345
-
โ โ 904dac9cd09e b
1346
-
โ โ D b
1347
-
โ โ 97233b506c11 e
1348
-
โ โ A e
1349
-
โ โ 8cbe1a629aed d
1350
-
โโโฏ D b
1351
-
โ A d
1352
-
โ c1fbbbe74a28 a
1353
-
โ A a
1354
-
โ A b
1355
-
โ 000000000000 (empty)
1356
-
[EOF]
1357
-
");
1358
-
let output = work_dir.run_jj(["file", "list", "-r=b"]);
1359
-
insta::assert_snapshot!(output, @r"
1360
-
a
1361
-
[EOF]
1362
-
");
1363
-
let output = work_dir.run_jj(["file", "list", "-r=c"]);
1364
-
insta::assert_snapshot!(output, @r"
1365
-
a
1366
-
b
1367
-
c
1368
-
[EOF]
1369
-
");
1370
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1371
-
insta::assert_snapshot!(output, @r"
1372
-
a
1373
-
d
1374
-
[EOF]
1375
-
");
1376
-
1377
-
// ========== Part 3 =========
1378
-
// Reminder of the setup
1379
-
test_env.advance_test_rng_seed_to_multiple_of(200_000);
1380
-
work_dir
1381
-
.run_jj(["operation", "restore", &beginning])
1382
-
.success();
1383
-
insta::assert_snapshot!(run_log(), @r"
1384
-
@ 42acd0537c88 f
1385
-
โโโฎ A f
1386
-
โ โ 4fb9706b0f47 c
1387
-
โ โ A c
1388
-
โ โ b1e1eea2f666 b
1389
-
โ โ A b
1390
-
โ โ b4e3197108ba e
1391
-
โ โ A e
1392
-
โ โ d707102f499f d
1393
-
โโโฏ A d
1394
-
โ 7468364c89fc a
1395
-
โ A a
1396
-
โ 000000000000 (empty)
1397
-
[EOF]
1398
-
");
1399
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1400
-
insta::assert_snapshot!(output, @r"
1401
-
a
1402
-
d
1403
-
[EOF]
1404
-
");
1405
-
let output = work_dir.run_jj(["file", "list", "-r=f"]);
1406
-
insta::assert_snapshot!(output, @r"
1407
-
a
1408
-
b
1409
-
c
1410
-
d
1411
-
e
1412
-
f
1413
-
[EOF]
1414
-
");
1415
-
1416
-
// --restore-descendants works when squashing from grandchild to grandparent
1417
-
work_dir
1418
-
.run_jj(["operation", "restore", &beginning])
1419
-
.success();
1420
-
let output = work_dir.run_jj(["squash", "--from=e", "--into=a", "--restore-descendants"]);
1421
-
insta::assert_snapshot!(output, @r"
1422
-
------- stderr -------
1423
-
Rebased 4 descendant commits (while preserving their content)
1424
-
Working copy (@) now at: kpqxywon 6d14c928 f | (no description set)
1425
-
Parent commit (@-) : yqosqzyt ab775412 d e | (no description set)
1426
-
Parent commit (@-) : mzvwutvl 175aa1f2 c | (no description set)
1427
-
[EOF]
1428
-
");
1429
-
insta::assert_snapshot!(run_log(), @r"
1430
-
@ 6d14c928f32e f
1431
-
โโโฎ A e
1432
-
โ โ A f
1433
-
โ โ 175aa1f28a05 c
1434
-
โ โ A c
1435
-
โ โ d1076aeca3e6 b
1436
-
โ โ A b
1437
-
โ โ D e
1438
-
โ โ ab7754126332 d e
1439
-
โโโฏ A d
1440
-
โ D e
1441
-
โ 4644e0c16443 a
1442
-
โ A a
1443
-
โ A e
1444
-
โ 000000000000 (empty)
1445
-
[EOF]
1446
-
");
1447
-
let output = work_dir.run_jj(["file", "list", "-r=b"]);
1448
-
insta::assert_snapshot!(output, @r"
1449
-
a
1450
-
b
1451
-
[EOF]
1452
-
");
1453
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1454
-
insta::assert_snapshot!(output, @r"
1455
-
a
1456
-
d
1457
-
[EOF]
1458
-
");
1459
-
let output = work_dir.run_jj(["file", "list", "-r=f"]);
1460
-
insta::assert_snapshot!(output, @r"
1461
-
a
1462
-
b
1463
-
c
1464
-
d
1465
-
e
1466
-
f
1467
-
[EOF]
1468
-
");
1469
-
1470
-
// --restore-descendants works when squashing from grandparent to grandchild
1471
-
work_dir
1472
-
.run_jj(["operation", "restore", &beginning])
1473
-
.success();
1474
-
let output = work_dir.run_jj(["squash", "--from=a", "--into=e", "--restore-descendants"]);
1475
-
insta::assert_snapshot!(output, @r"
1476
-
------- stderr -------
1477
-
Rebased 1 descendant commits (while preserving their content)
1478
-
Working copy (@) now at: kpqxywon 94ad7042 f | (no description set)
1479
-
Parent commit (@-) : yostqsxw 582d640e e | (no description set)
1480
-
Parent commit (@-) : mzvwutvl 2214436c c | (no description set)
1481
-
[EOF]
1482
-
");
1483
-
insta::assert_snapshot!(run_log(), @r"
1484
-
@ 94ad70428c4a f
1485
-
โโโฎ A f
1486
-
โ โ 2214436c3fa7 c
1487
-
โ โ A c
1488
-
โ โ a469c893f362 b
1489
-
โ โ A a
1490
-
โ โ A b
1491
-
โ โ 582d640e331f e
1492
-
โ โ A e
1493
-
โ โ 93671eb30330 d
1494
-
โโโฏ A a
1495
-
โ A d
1496
-
โ 000000000000 a (empty)
1497
-
[EOF]
1498
-
");
1499
-
let output = work_dir.run_jj(["file", "list", "-r=b"]);
1500
-
insta::assert_snapshot!(output, @r"
1501
-
a
1502
-
b
1503
-
[EOF]
1504
-
");
1505
-
let output = work_dir.run_jj(["file", "list", "-r=d"]);
1506
-
insta::assert_snapshot!(output, @r"
1507
-
a
1508
-
d
1509
-
[EOF]
1510
-
");
1511
-
let output = work_dir.run_jj(["file", "list", "-r=f"]);
1512
-
insta::assert_snapshot!(output, @r"
1513
-
a
1514
-
b
1515
-
c
1516
-
d
1517
-
e
1518
-
f
1519
-
[EOF]
1520
-
");
1521
-
}
1522
-
1523
-
#[test]
1524
749
fn test_squash_from_multiple() {
1525
750
let test_env = TestEnvironment::default();
1526
751
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
+8
-3
docs/releasing.md
+8
-3
docs/releasing.md
···
13
13
to run something like this:
14
14
15
15
```shell
16
-
root=$(jj log --no-graph -r 'heads(tags(glob:"v*.*.*") & ::trunk())' -T 'commit_id')
17
-
gh api "/repos/jj-vcs/jj/compare/$root...main" --paginate \
18
-
| jq -r '.commits[] | select(.author.login | endswith("[bot]") | not) | "* " + .commit.author.name + " (@" + .author.login + ")"' | sort -fu
16
+
root=$(jj log --no-graph -r 'heads(tags(glob:"v*.*.*") & ::trunk())' -T commit_id)
17
+
filter='
18
+
map(.commits[] | select(.author.login | endswith("[bot]") | not))
19
+
| unique_by(.author.login)
20
+
| map("* \(.commit.author.name) (@\(.author.login))"))
21
+
| .[]
22
+
'
23
+
gh api "/repos/jj-vcs/jj/compare/$root...main" --paginate | jq -sr "$filter" | sort -f
19
24
```
20
25
21
26
https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits
+28
-9
lib/src/config_resolver.rs
+28
-9
lib/src/config_resolver.rs
···
27
27
use crate::config::ConfigGetError;
28
28
use crate::config::ConfigLayer;
29
29
use crate::config::ConfigNamePathBuf;
30
+
use crate::config::ConfigSource;
30
31
use crate::config::ConfigUpdateError;
31
32
use crate::config::ConfigValue;
32
33
use crate::config::StackedConfig;
···
367
368
pub fn migrate(
368
369
config: &mut StackedConfig,
369
370
rules: &[ConfigMigrationRule],
370
-
) -> Result<Vec<String>, ConfigMigrateError> {
371
+
) -> Result<Vec<(ConfigSource, String)>, ConfigMigrateError> {
371
372
let mut descriptions = Vec::new();
372
373
for layer in config.layers_mut() {
373
374
migrate_layer(layer, rules, &mut descriptions)
···
379
380
fn migrate_layer(
380
381
layer: &mut Arc<ConfigLayer>,
381
382
rules: &[ConfigMigrationRule],
382
-
descriptions: &mut Vec<String>,
383
+
descriptions: &mut Vec<(ConfigSource, String)>,
383
384
) -> Result<(), ConfigMigrateLayerError> {
384
385
let rules_to_apply = rules
385
386
.iter()
···
391
392
let layer_mut = Arc::make_mut(layer);
392
393
for rule in rules_to_apply {
393
394
let desc = rule.apply(layer_mut)?;
394
-
descriptions.push(desc);
395
+
descriptions.push((layer_mut.source, desc));
395
396
}
396
397
Ok(())
397
398
}
···
862
863
let descriptions = migrate(&mut config, &rules).unwrap();
863
864
insta::assert_debug_snapshot!(descriptions, @r#"
864
865
[
865
-
"foo.old is renamed to foo.new",
866
-
"bar.old is deleted (superseded by baz.new)",
867
-
"bar.old is renamed to baz.new",
866
+
(
867
+
User,
868
+
"foo.old is renamed to foo.new",
869
+
),
870
+
(
871
+
User,
872
+
"bar.old is deleted (superseded by baz.new)",
873
+
),
874
+
(
875
+
User,
876
+
"bar.old is renamed to baz.new",
877
+
),
868
878
]
869
879
"#);
870
880
insta::assert_snapshot!(config.layers()[0].data, @r"
···
913
923
let descriptions = migrate(&mut config, &rules).unwrap();
914
924
insta::assert_debug_snapshot!(descriptions, @r#"
915
925
[
916
-
"foo.old is updated to foo.new = ['foo.old #0']",
917
-
"bar.old is deleted (superseded by baz.new)",
918
-
"bar.old is updated to baz.new = \"bar.old #1 updated\"",
926
+
(
927
+
User,
928
+
"foo.old is updated to foo.new = ['foo.old #0']",
929
+
),
930
+
(
931
+
User,
932
+
"bar.old is deleted (superseded by baz.new)",
933
+
),
934
+
(
935
+
User,
936
+
"bar.old is updated to baz.new = \"bar.old #1 updated\"",
937
+
),
919
938
]
920
939
"#);
921
940
insta::assert_snapshot!(config.layers()[0].data, @r"
+40
-26
lib/src/default_index/store.rs
+40
-26
lib/src/default_index/store.rs
···
15
15
#![allow(missing_docs)]
16
16
17
17
use std::any::Any;
18
-
use std::collections::HashSet;
18
+
use std::collections::HashMap;
19
19
use std::fs;
20
20
use std::io;
21
21
use std::io::Write as _;
22
22
use std::path::Path;
23
23
use std::path::PathBuf;
24
+
use std::slice;
24
25
use std::sync::Arc;
25
26
26
27
use itertools::Itertools as _;
···
49
50
use crate::object_id::ObjectId as _;
50
51
use crate::op_store::OpStoreError;
51
52
use crate::op_store::OperationId;
53
+
use crate::op_walk;
52
54
use crate::operation::Operation;
53
55
use crate::store::Store;
54
56
···
185
187
operation: &Operation,
186
188
store: &Arc<Store>,
187
189
) -> Result<Arc<ReadonlyIndexSegment>, DefaultIndexStoreError> {
188
-
let view = operation.view()?;
190
+
tracing::info!("scanning operations to index");
189
191
let operations_dir = self.operations_dir();
190
192
let commit_id_length = store.commit_id_length();
191
193
let change_id_length = store.change_id_length();
192
-
let mut visited_heads: HashSet<CommitId> =
193
-
view.all_referenced_commit_ids().cloned().collect();
194
-
let mut historical_heads: Vec<(CommitId, OperationId)> = visited_heads
194
+
let ops_to_visit: Vec<_> =
195
+
op_walk::walk_ancestors(slice::from_ref(operation)).try_collect()?;
196
+
// Pick the latest existing ancestor operation as the parent segment.
197
+
let parent_op = ops_to_visit
195
198
.iter()
196
-
.map(|commit_id| (commit_id.clone(), operation.id().clone()))
197
-
.collect();
198
-
let mut parent_op_id: Option<OperationId> = None;
199
-
for op in dag_walk::dfs_ok(
200
-
[Ok(operation.clone())],
201
-
|op: &Operation| op.id().clone(),
202
-
|op: &Operation| op.parents().collect_vec(),
203
-
) {
204
-
let op = op?;
205
-
// Pick the latest existing ancestor operation as the parent
206
-
// segment. Perhaps, breadth-first search is more appropriate here,
207
-
// but that wouldn't matter in practice as the operation log is
208
-
// mostly linear.
209
-
if parent_op_id.is_none() && operations_dir.join(op.id().hex()).is_file() {
210
-
parent_op_id = Some(op.id().clone());
199
+
.find(|op| operations_dir.join(op.id().hex()).is_file())
200
+
.cloned();
201
+
// Remove ancestors of the latest existing operation, which should have
202
+
// been indexed in the parent segment. This could be optimized for
203
+
// linear history, but parent_op is often None.
204
+
let ops_to_visit = if let Some(op) = &parent_op {
205
+
let mut wanted_ops: HashMap<&OperationId, &Operation> =
206
+
ops_to_visit.iter().map(|op| (op.id(), op)).collect();
207
+
let mut work = vec![op.id()];
208
+
while let Some(id) = work.pop() {
209
+
if let Some(op) = wanted_ops.remove(id) {
210
+
work.extend(op.parent_ids());
211
+
}
211
212
}
212
-
// TODO: no need to walk ancestors of the parent_op_id operation
213
+
ops_to_visit
214
+
.iter()
215
+
.filter(|op| wanted_ops.contains_key(op.id()))
216
+
.cloned()
217
+
.collect()
218
+
} else {
219
+
ops_to_visit
220
+
};
221
+
tracing::info!(
222
+
ops_count = ops_to_visit.len(),
223
+
"collecting head commits to index"
224
+
);
225
+
let mut historical_heads: HashMap<CommitId, OperationId> = HashMap::new();
226
+
for op in &ops_to_visit {
213
227
for commit_id in op.view()?.all_referenced_commit_ids() {
214
-
if visited_heads.insert(commit_id.clone()) {
215
-
historical_heads.push((commit_id.clone(), op.id().clone()));
228
+
if !historical_heads.contains_key(commit_id) {
229
+
historical_heads.insert(commit_id.clone(), op.id().clone());
216
230
}
217
231
}
218
232
}
219
233
let maybe_parent_file;
220
234
let mut mutable_index;
221
-
match parent_op_id {
235
+
match &parent_op {
222
236
None => {
223
237
maybe_parent_file = None;
224
238
mutable_index = DefaultMutableIndex::full(commit_id_length, change_id_length);
225
239
}
226
-
Some(parent_op_id) => {
240
+
Some(op) => {
227
241
let parent_file = self.load_index_segments_at_operation(
228
-
&parent_op_id,
242
+
op.id(),
229
243
commit_id_length,
230
244
change_id_length,
231
245
)?;
+4
-19
lib/src/repo.rs
+4
-19
lib/src/repo.rs
···
1406
1406
/// The content of those descendants will remain untouched.
1407
1407
/// Returns the number of reparented descendants.
1408
1408
pub fn reparent_descendants(&mut self) -> BackendResult<usize> {
1409
+
let roots = self.parent_mapping.keys().cloned().collect_vec();
1409
1410
let mut num_reparented = 0;
1410
-
self.reparent_descendants_with_progress(|_, _| {
1411
-
num_reparented += 1;
1412
-
})?;
1413
-
Ok(num_reparented)
1414
-
}
1415
-
1416
-
/// Reparent descendants, and call the provided function for each moved
1417
-
/// commit
1418
-
///
1419
-
/// The function takes the old commit and the reparented commit.
1420
-
pub fn reparent_descendants_with_progress(
1421
-
&mut self,
1422
-
mut progress: impl FnMut(Commit, Commit),
1423
-
) -> BackendResult<()> {
1424
-
let roots = self.parent_mapping.keys().cloned().collect_vec();
1425
1411
self.transform_descendants(roots, |rewriter| {
1426
1412
if rewriter.parents_changed() {
1427
-
let old_commit = rewriter.old_commit().clone();
1428
1413
let builder = rewriter.reparent();
1429
-
let reparented_commit = builder.write()?;
1430
-
progress(old_commit, reparented_commit);
1414
+
builder.write()?;
1415
+
num_reparented += 1;
1431
1416
}
1432
1417
Ok(())
1433
1418
})?;
1434
1419
self.parent_mapping.clear();
1435
-
Ok(())
1420
+
Ok(num_reparented)
1436
1421
}
1437
1422
1438
1423
pub fn set_wc_commit(
+11
-29
lib/src/rewrite.rs
+11
-29
lib/src/rewrite.rs
···
1109
1109
pub abandoned_commits: Vec<Commit>,
1110
1110
}
1111
1111
1112
-
#[derive(Clone, Debug)]
1113
-
pub struct SquashOptions {
1114
-
pub keep_emptied: bool,
1115
-
pub restore_descendants: bool,
1116
-
}
1117
-
1118
1112
/// Squash `sources` into `destination` and return a [`SquashedCommit`] for the
1119
1113
/// resulting commit. Caller is responsible for setting the description and
1120
1114
/// finishing the commit.
···
1122
1116
repo: &'repo mut MutableRepo,
1123
1117
sources: &[CommitWithSelection],
1124
1118
destination: &Commit,
1125
-
SquashOptions {
1126
-
keep_emptied,
1127
-
restore_descendants,
1128
-
}: SquashOptions,
1119
+
keep_emptied: bool,
1129
1120
) -> BackendResult<Option<SquashedCommit<'repo>>> {
1130
1121
struct SourceCommit<'a> {
1131
1122
commit: &'a CommitWithSelection,
···
1178
1169
// rewritten sources. Otherwise it will likely already have the content
1179
1170
// changes we're moving, so applying them will have no effect and the
1180
1171
// changes will disappear.
1181
-
if restore_descendants {
1182
-
repo.reparent_descendants_with_progress(|old_commit, rebased_commit| {
1183
-
if old_commit.id() != destination.id() {
1184
-
return;
1185
-
}
1186
-
rewritten_destination = rebased_commit;
1187
-
})?;
1188
-
} else {
1189
-
let options = RebaseOptions::default();
1190
-
repo.rebase_descendants_with_options(&options, |old_commit, rebased_commit| {
1191
-
if old_commit.id() != destination.id() {
1192
-
return;
1193
-
}
1194
-
rewritten_destination = match rebased_commit {
1195
-
RebasedCommit::Rewritten(commit) => commit,
1196
-
RebasedCommit::Abandoned { .. } => panic!("all commits should be kept"),
1197
-
};
1198
-
})?;
1199
-
}
1172
+
let options = RebaseOptions::default();
1173
+
repo.rebase_descendants_with_options(&options, |old_commit, rebased_commit| {
1174
+
if old_commit.id() != destination.id() {
1175
+
return;
1176
+
}
1177
+
rewritten_destination = match rebased_commit {
1178
+
RebasedCommit::Rewritten(commit) => commit,
1179
+
RebasedCommit::Abandoned { .. } => panic!("all commits should be kept"),
1180
+
};
1181
+
})?;
1200
1182
}
1201
1183
// Apply the selected changes onto the destination
1202
1184
let mut destination_tree = rewritten_destination.tree()?;