just playing with tangled
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at diffedit3 124 lines 4.7 kB view raw
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 std::io::{self, Write}; 16 17use jj_lib::backend::BackendResult; 18use jj_lib::conflicts::{materialize_tree_value, MaterializedTreeValue}; 19use jj_lib::fileset::{FilePattern, FilesetExpression}; 20use jj_lib::merge::MergedTreeValue; 21use jj_lib::repo::Repo; 22use jj_lib::repo_path::RepoPath; 23use pollster::FutureExt; 24use tracing::instrument; 25 26use crate::cli_util::{ 27 print_unmatched_explicit_paths, CommandHelper, RevisionArg, WorkspaceCommandHelper, 28}; 29use crate::command_error::{user_error, CommandError}; 30use crate::ui::Ui; 31 32/// Print contents of files in a revision 33/// 34/// If the given path is a directory, files in the directory will be visited 35/// recursively. 36#[derive(clap::Args, Clone, Debug)] 37pub(crate) struct CatArgs { 38 /// The revision to get the file contents from 39 #[arg(long, short, default_value = "@")] 40 revision: RevisionArg, 41 /// Paths to print 42 #[arg(required = true, value_hint = clap::ValueHint::FilePath)] 43 paths: Vec<String>, 44} 45 46#[instrument(skip_all)] 47pub(crate) fn cmd_cat( 48 ui: &mut Ui, 49 command: &CommandHelper, 50 args: &CatArgs, 51) -> Result<(), CommandError> { 52 let workspace_command = command.workspace_helper(ui)?; 53 let commit = workspace_command.resolve_single_rev(&args.revision)?; 54 let tree = commit.tree()?; 55 // TODO: No need to add special case for empty paths when switching to 56 // parse_union_filesets(). paths = [] should be "none()" if supported. 57 let fileset_expression = workspace_command.parse_file_patterns(&args.paths)?; 58 59 // Try fast path for single file entry 60 if let Some(path) = get_single_path(&fileset_expression) { 61 let value = tree.path_value(path); 62 if value.is_absent() { 63 let ui_path = workspace_command.format_file_path(path); 64 return Err(user_error(format!("No such path: {ui_path}"))); 65 } 66 if !value.is_tree() { 67 ui.request_pager(); 68 write_tree_entries(ui, &workspace_command, [(path, Ok(value))])?; 69 return Ok(()); 70 } 71 } 72 73 let matcher = fileset_expression.to_matcher(); 74 ui.request_pager(); 75 write_tree_entries( 76 ui, 77 &workspace_command, 78 tree.entries_matching(matcher.as_ref()), 79 )?; 80 print_unmatched_explicit_paths(ui, &workspace_command, &fileset_expression, [&tree])?; 81 Ok(()) 82} 83 84fn get_single_path(expression: &FilesetExpression) -> Option<&RepoPath> { 85 match &expression { 86 FilesetExpression::Pattern(pattern) => match pattern { 87 // Not using pattern.as_path() because files-in:<path> shouldn't 88 // select the literal <path> itself. 89 FilePattern::FilePath(path) | FilePattern::PrefixPath(path) => Some(path), 90 FilePattern::FileGlob { .. } => None, 91 }, 92 _ => None, 93 } 94} 95 96fn write_tree_entries<P: AsRef<RepoPath>>( 97 ui: &Ui, 98 workspace_command: &WorkspaceCommandHelper, 99 entries: impl IntoIterator<Item = (P, BackendResult<MergedTreeValue>)>, 100) -> Result<(), CommandError> { 101 let repo = workspace_command.repo(); 102 for (path, result) in entries { 103 let value = result?; 104 let materialized = materialize_tree_value(repo.store(), path.as_ref(), value).block_on()?; 105 match materialized { 106 MaterializedTreeValue::Absent => panic!("absent values should be excluded"), 107 MaterializedTreeValue::File { mut reader, .. } => { 108 io::copy(&mut reader, &mut ui.stdout_formatter().as_mut())?; 109 } 110 MaterializedTreeValue::Conflict { contents, .. } => { 111 ui.stdout_formatter().write_all(&contents)?; 112 } 113 MaterializedTreeValue::Symlink { .. } | MaterializedTreeValue::GitSubmodule(_) => { 114 let ui_path = workspace_command.format_file_path(path.as_ref()); 115 writeln!( 116 ui.warning_default(), 117 "Path exists but is not a file: {ui_path}" 118 )?; 119 } 120 MaterializedTreeValue::Tree(_) => panic!("entries should not contain trees"), 121 } 122 } 123 Ok(()) 124}