just playing with tangled
1// Copyright 2020 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use jj_lib::rewrite::merge_commit_trees;
16use tracing::instrument;
17
18use crate::cli_util::{print_unmatched_explicit_paths, CommandHelper, RevisionArg};
19use crate::command_error::CommandError;
20use crate::diff_util::{diff_formats_for, show_diff, DiffFormatArgs};
21use crate::ui::Ui;
22
23/// Compare file contents between two revisions
24///
25/// With the `-r` option, which is the default, shows the changes compared to
26/// the parent revision. If there are several parent revisions (i.e., the given
27/// revision is a merge), then they will be merged and the changes from the
28/// result to the given revision will be shown.
29///
30/// With the `--from` and/or `--to` options, shows the difference from/to the
31/// given revisions. If either is left out, it defaults to the working-copy
32/// commit. For example, `jj diff --from main` shows the changes from "main"
33/// (perhaps a branch name) to the working-copy commit.
34#[derive(clap::Args, Clone, Debug)]
35pub(crate) struct DiffArgs {
36 /// Show changes in this revision, compared to its parent(s)
37 ///
38 /// If the revision is a merge commit, this shows changes *from* the
39 /// automatic merge of the contents of all of its parents *to* the contents
40 /// of the revision itself.
41 #[arg(long, short)]
42 revision: Option<RevisionArg>,
43 /// Show changes from this revision
44 #[arg(long, conflicts_with = "revision")]
45 from: Option<RevisionArg>,
46 /// Show changes to this revision
47 #[arg(long, conflicts_with = "revision")]
48 to: Option<RevisionArg>,
49 /// Restrict the diff to these paths
50 #[arg(value_hint = clap::ValueHint::AnyPath)]
51 paths: Vec<String>,
52 #[command(flatten)]
53 format: DiffFormatArgs,
54}
55
56#[instrument(skip_all)]
57pub(crate) fn cmd_diff(
58 ui: &mut Ui,
59 command: &CommandHelper,
60 args: &DiffArgs,
61) -> Result<(), CommandError> {
62 let workspace_command = command.workspace_helper(ui)?;
63 let from_tree;
64 let to_tree;
65 if args.from.is_some() || args.to.is_some() {
66 let from =
67 workspace_command.resolve_single_rev(args.from.as_ref().unwrap_or(&RevisionArg::AT))?;
68 from_tree = from.tree()?;
69 let to =
70 workspace_command.resolve_single_rev(args.to.as_ref().unwrap_or(&RevisionArg::AT))?;
71 to_tree = to.tree()?;
72 } else {
73 let commit = workspace_command
74 .resolve_single_rev(args.revision.as_ref().unwrap_or(&RevisionArg::AT))?;
75 let parents = commit.parents();
76 from_tree = merge_commit_trees(workspace_command.repo().as_ref(), &parents)?;
77 to_tree = commit.tree()?
78 }
79 let fileset_expression = workspace_command.parse_file_patterns(&args.paths)?;
80 let matcher = fileset_expression.to_matcher();
81 let diff_formats = diff_formats_for(command.settings(), &args.format)?;
82 ui.request_pager();
83 show_diff(
84 ui,
85 ui.stdout_formatter().as_mut(),
86 &workspace_command,
87 &from_tree,
88 &to_tree,
89 matcher.as_ref(),
90 &diff_formats,
91 )?;
92 print_unmatched_explicit_paths(
93 ui,
94 &workspace_command,
95 &fileset_expression,
96 [&from_tree, &to_tree],
97 )?;
98 Ok(())
99}