mirror of
https://github.com/martinvonz/jj.git
synced 2025-05-05 15:32:49 +00:00
Compare commits
4 Commits
0eceed9832
...
b568bb67f0
Author | SHA1 | Date | |
---|---|---|---|
|
b568bb67f0 | ||
|
6f6496ba83 | ||
|
f9966a644b | ||
|
19f997a466 |
@ -36,6 +36,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
[`templates.draft_commit_description`](docs/config.md#default-description),
|
||||
and/or [`templates.commit_trailers`](docs/config.md#commit-trailers).
|
||||
|
||||
* On macOS, config.toml files in `~/Library/Application Support/jj` are
|
||||
deprecated; one should instead use `$XDG_CONFIG_HOME/jj`
|
||||
(defaults to `~/.config/jj`)
|
||||
|
||||
### New features
|
||||
|
||||
* Color-words diff has gained [an option to compare conflict pairs without
|
||||
@ -81,6 +85,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
* Fixed bash and zsh shell completion when completing aliases of multiple arguments.
|
||||
[#5377](https://github.com/jj-vcs/jj/issues/5377)
|
||||
|
||||
* On macOS, jj now defaults to looking for its config in `$XDG_CONFIG_HOME`
|
||||
(`~/.config` by default) rather than the more GUI-native
|
||||
`~/Library/Application Support`.
|
||||
|
||||
### Packaging changes
|
||||
|
||||
* Jujutsu now uses
|
||||
|
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -852,6 +852,17 @@ version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6"
|
||||
|
||||
[[package]]
|
||||
name = "etcetera"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c7b13d0780cb82722fd59f6f57f925e143427e4a75313a6c77243bf5326ae6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"home",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.22.11"
|
||||
@ -2352,8 +2363,8 @@ dependencies = [
|
||||
"criterion",
|
||||
"crossterm",
|
||||
"datatest-stable",
|
||||
"dirs",
|
||||
"dunce",
|
||||
"etcetera",
|
||||
"futures 0.3.31",
|
||||
"git2",
|
||||
"gix",
|
||||
|
@ -41,8 +41,8 @@ criterion = "0.5.1"
|
||||
crossterm = { version = "0.28", default-features = false, features = ["windows"] }
|
||||
datatest-stable = "0.3.2"
|
||||
digest = "0.10.7"
|
||||
dirs = "6.0.0"
|
||||
dunce = "1.0.5"
|
||||
etcetera = "0.10.0"
|
||||
either = "1.15.0"
|
||||
futures = "0.3.31"
|
||||
git2 = { version = "0.20.1", features = [
|
||||
|
@ -62,8 +62,8 @@ clap_complete_nushell = { workspace = true }
|
||||
clap_mangen = { workspace = true }
|
||||
criterion = { workspace = true, optional = true }
|
||||
crossterm = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
dunce = { workspace = true }
|
||||
etcetera = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
git2 = { workspace = true, optional = true }
|
||||
gix = { workspace = true, optional = true }
|
||||
|
@ -3873,7 +3873,7 @@ impl<'a> CliRunner<'a> {
|
||||
"Did you update to a commit where the directory doesn't exist?",
|
||||
)
|
||||
})?;
|
||||
let mut config_env = ConfigEnv::from_environment();
|
||||
let mut config_env = ConfigEnv::from_environment(ui);
|
||||
let mut last_config_migration_descriptions = Vec::new();
|
||||
let mut migrate_config = |config: &mut StackedConfig| -> Result<(), CommandError> {
|
||||
last_config_migration_descriptions =
|
||||
|
@ -838,7 +838,7 @@ fn get_jj_command() -> Result<(JjBuilder, UserSettings), CommandError> {
|
||||
.and_then(dunce::canonicalize)
|
||||
.map_err(user_error)?;
|
||||
// No config migration for completion. Simply ignore deprecated variables.
|
||||
let mut config_env = ConfigEnv::from_environment();
|
||||
let mut config_env = ConfigEnv::from_environment(&ui);
|
||||
let maybe_cwd_workspace_loader = DefaultWorkspaceLoaderFactory.create(find_workspace_dir(&cwd));
|
||||
let _ = config_env.reload_user_config(&mut raw_config);
|
||||
if let Ok(loader) = &maybe_cwd_workspace_loader {
|
||||
|
@ -22,6 +22,7 @@ use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use etcetera::BaseStrategy as _;
|
||||
use itertools::Itertools as _;
|
||||
use jj_lib::config::ConfigFile;
|
||||
use jj_lib::config::ConfigGetError;
|
||||
@ -42,6 +43,7 @@ use crate::command_error::config_error;
|
||||
use crate::command_error::config_error_with_message;
|
||||
use crate::command_error::CommandError;
|
||||
use crate::text_util;
|
||||
use crate::ui::Ui;
|
||||
|
||||
// TODO(#879): Consider generating entire schema dynamically vs. static file.
|
||||
pub const CONFIG_SCHEMA: &str = include_str!("config-schema.json");
|
||||
@ -163,26 +165,38 @@ impl AsMut<StackedConfig> for RawConfig {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum ConfigPath {
|
||||
/// Existing config file path.
|
||||
Existing(PathBuf),
|
||||
/// Could not find any config file, but a new file can be created at the
|
||||
/// specified location.
|
||||
New(PathBuf),
|
||||
enum ConfigPathState {
|
||||
New,
|
||||
Exists,
|
||||
}
|
||||
|
||||
/// A ConfigPath can be in one of two states:
|
||||
///
|
||||
/// - exists(): a config file exists at the path
|
||||
/// - !exists(): a config file doesn't exist here, but a new file _can_ be
|
||||
/// created at this path
|
||||
#[derive(Clone, Debug)]
|
||||
struct ConfigPath {
|
||||
path: PathBuf,
|
||||
state: ConfigPathState,
|
||||
}
|
||||
|
||||
impl ConfigPath {
|
||||
fn new(path: PathBuf) -> Self {
|
||||
if path.exists() {
|
||||
ConfigPath::Existing(path)
|
||||
} else {
|
||||
ConfigPath::New(path)
|
||||
use ConfigPathState::*;
|
||||
ConfigPath {
|
||||
state: if path.exists() { Exists } else { New },
|
||||
path,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_path(&self) -> &Path {
|
||||
match self {
|
||||
ConfigPath::Existing(path) | ConfigPath::New(path) => path,
|
||||
&self.path
|
||||
}
|
||||
fn exists(&self) -> bool {
|
||||
match self.state {
|
||||
ConfigPathState::Exists => true,
|
||||
ConfigPathState::New => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,18 +218,21 @@ fn create_dir_all(path: &Path) -> std::io::Result<()> {
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct UnresolvedConfigEnv {
|
||||
config_dir: Option<PathBuf>,
|
||||
// TODO: remove after jj 0.35
|
||||
macos_legacy_config_dir: Option<PathBuf>,
|
||||
home_dir: Option<PathBuf>,
|
||||
jj_config: Option<String>,
|
||||
}
|
||||
|
||||
impl UnresolvedConfigEnv {
|
||||
fn resolve(self) -> Vec<ConfigPath> {
|
||||
fn resolve(self, ui: &Ui) -> Vec<ConfigPath> {
|
||||
if let Some(paths) = self.jj_config {
|
||||
return split_paths(&paths)
|
||||
.filter(|path| !path.as_os_str().is_empty())
|
||||
.map(ConfigPath::new)
|
||||
.collect();
|
||||
}
|
||||
|
||||
let mut paths = vec![];
|
||||
let home_config_path = self.home_dir.map(|mut home_dir| {
|
||||
home_dir.push(".jjconfig.toml");
|
||||
@ -231,22 +248,77 @@ impl UnresolvedConfigEnv {
|
||||
config_dir.push("conf.d");
|
||||
ConfigPath::new(config_dir)
|
||||
});
|
||||
use ConfigPath::*;
|
||||
if let Some(path @ Existing(_)) = home_config_path {
|
||||
paths.push(path);
|
||||
} else if let (Some(path @ New(_)), None) = (home_config_path, &platform_config_path) {
|
||||
paths.push(path);
|
||||
let legacy_platform_config_path =
|
||||
self.macos_legacy_config_dir.clone().map(|mut config_dir| {
|
||||
config_dir.push("jj");
|
||||
config_dir.push("config.toml");
|
||||
ConfigPath::new(config_dir)
|
||||
});
|
||||
let legacy_platform_config_dir = self.macos_legacy_config_dir.map(|mut config_dir| {
|
||||
config_dir.push("jj");
|
||||
config_dir.push("conf.d");
|
||||
ConfigPath::new(config_dir)
|
||||
});
|
||||
|
||||
if let Some(path) = home_config_path {
|
||||
if path.exists()
|
||||
|| (platform_config_path.is_none() && legacy_platform_config_path.is_none())
|
||||
{
|
||||
paths.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
// This should be the default config created if there's
|
||||
// no user config and `jj config edit` is executed.
|
||||
if let Some(path) = platform_config_path {
|
||||
paths.push(path);
|
||||
}
|
||||
if let Some(path @ Existing(_)) = platform_config_dir {
|
||||
paths.push(path);
|
||||
|
||||
// theoretically these should be an `if let Some(...) = ... && ..., but that
|
||||
// isn't stable
|
||||
if let Some(path) = platform_config_dir {
|
||||
if path.exists() {
|
||||
paths.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(path) = legacy_platform_config_path {
|
||||
if path.exists() {
|
||||
Self::warn_for_deprecated_path(
|
||||
ui,
|
||||
path.as_path(),
|
||||
"~/Library/Application Support",
|
||||
"~/.config",
|
||||
);
|
||||
paths.push(path);
|
||||
}
|
||||
}
|
||||
if let Some(path) = legacy_platform_config_dir {
|
||||
if path.exists() {
|
||||
Self::warn_for_deprecated_path(
|
||||
ui,
|
||||
path.as_path(),
|
||||
"~/Library/Application Support",
|
||||
"~/.config",
|
||||
);
|
||||
paths.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
fn warn_for_deprecated_path(ui: &Ui, path: &Path, old: &str, new: &str) {
|
||||
let _ = indoc::writedoc!(
|
||||
ui.warning_default(),
|
||||
r"
|
||||
Deprecated configuration file `{}`.
|
||||
Configuration files in `{old}` are deprecated, and support will be removed in a future release.
|
||||
Instead, move your configuration files to `{new}`.
|
||||
",
|
||||
path.display(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -260,19 +332,41 @@ pub struct ConfigEnv {
|
||||
|
||||
impl ConfigEnv {
|
||||
/// Initializes configuration loader based on environment variables.
|
||||
pub fn from_environment() -> Self {
|
||||
pub fn from_environment(ui: &Ui) -> Self {
|
||||
let config_dir = etcetera::choose_base_strategy()
|
||||
.ok()
|
||||
.map(|s| s.config_dir());
|
||||
|
||||
// older versions of jj used a more "GUI" config option,
|
||||
// which is not designed for user-editable configuration of CLI utilities.
|
||||
let macos_legacy_config_dir = if cfg!(target_os = "macos") {
|
||||
etcetera::base_strategy::choose_native_strategy()
|
||||
.ok()
|
||||
.map(|s| {
|
||||
// note that etcetera calls Library/Application Support the "data dir",
|
||||
// Library/Preferences is supposed to be exclusively plists
|
||||
s.data_dir()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Canonicalize home as we do canonicalize cwd in CliRunner. $HOME might
|
||||
// point to symlink.
|
||||
let home_dir = dirs::home_dir().map(|path| dunce::canonicalize(&path).unwrap_or(path));
|
||||
let home_dir = etcetera::home_dir()
|
||||
.ok()
|
||||
.map(|d| dunce::canonicalize(&d).unwrap_or(d));
|
||||
|
||||
let env = UnresolvedConfigEnv {
|
||||
config_dir: dirs::config_dir(),
|
||||
config_dir,
|
||||
macos_legacy_config_dir,
|
||||
home_dir: home_dir.clone(),
|
||||
jj_config: env::var("JJ_CONFIG").ok(),
|
||||
};
|
||||
ConfigEnv {
|
||||
home_dir,
|
||||
repo_path: None,
|
||||
user_config_paths: env.resolve(),
|
||||
user_config_paths: env.resolve(ui),
|
||||
repo_config_path: None,
|
||||
command: None,
|
||||
}
|
||||
@ -284,16 +378,16 @@ impl ConfigEnv {
|
||||
|
||||
/// Returns the paths to the user-specific config files or directories.
|
||||
pub fn user_config_paths(&self) -> impl Iterator<Item = &Path> {
|
||||
self.user_config_paths.iter().map(|p| p.as_path())
|
||||
self.user_config_paths.iter().map(ConfigPath::as_path)
|
||||
}
|
||||
|
||||
/// Returns the paths to the existing user-specific config files or
|
||||
/// directories.
|
||||
pub fn existing_user_config_paths(&self) -> impl Iterator<Item = &Path> {
|
||||
self.user_config_paths.iter().filter_map(|p| match p {
|
||||
ConfigPath::Existing(path) => Some(path.as_path()),
|
||||
_ => None,
|
||||
})
|
||||
self.user_config_paths
|
||||
.iter()
|
||||
.filter(|p| p.exists())
|
||||
.map(ConfigPath::as_path)
|
||||
}
|
||||
|
||||
/// Returns user configuration files for modification. Instantiates one if
|
||||
@ -349,13 +443,13 @@ impl ConfigEnv {
|
||||
|
||||
/// Returns a path to the repo-specific config file.
|
||||
pub fn repo_config_path(&self) -> Option<&Path> {
|
||||
self.repo_config_path.as_ref().map(ConfigPath::as_path)
|
||||
self.repo_config_path.as_ref().map(|p| p.as_path())
|
||||
}
|
||||
|
||||
/// Returns a path to the existing repo-specific config file.
|
||||
fn existing_repo_config_path(&self) -> Option<&Path> {
|
||||
match &self.repo_config_path {
|
||||
Some(ConfigPath::Existing(path)) => Some(path),
|
||||
match self.repo_config_path {
|
||||
Some(ref path) if path.exists() => Some(path.as_path()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -1256,12 +1350,42 @@ mod tests {
|
||||
struct TestCase {
|
||||
files: &'static [&'static str],
|
||||
env: UnresolvedConfigEnv,
|
||||
wants: &'static [Want],
|
||||
wants: Vec<Want>,
|
||||
}
|
||||
|
||||
enum Want {
|
||||
New(&'static str),
|
||||
Existing(&'static str),
|
||||
#[derive(Debug)]
|
||||
enum WantState {
|
||||
New,
|
||||
Existing,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Want {
|
||||
path: &'static str,
|
||||
state: WantState,
|
||||
}
|
||||
|
||||
impl Want {
|
||||
const fn new(path: &'static str) -> Want {
|
||||
Want {
|
||||
path,
|
||||
state: WantState::New,
|
||||
}
|
||||
}
|
||||
|
||||
const fn existing(path: &'static str) -> Want {
|
||||
Want {
|
||||
path,
|
||||
state: WantState::Existing,
|
||||
}
|
||||
}
|
||||
|
||||
fn rooted_path(&self, root: &Path) -> PathBuf {
|
||||
root.join(self.path)
|
||||
}
|
||||
|
||||
fn exists(&self) -> bool {
|
||||
matches!(self.state, WantState::Existing)
|
||||
}
|
||||
}
|
||||
|
||||
fn config_path_home_existing() -> TestCase {
|
||||
@ -1271,7 +1395,7 @@ mod tests {
|
||||
home_dir: Some("home".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::Existing("home/.jjconfig.toml")],
|
||||
wants: vec![Want::existing("home/.jjconfig.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1282,7 +1406,7 @@ mod tests {
|
||||
home_dir: Some("home".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::New("home/.jjconfig.toml")],
|
||||
wants: vec![Want::new("home/.jjconfig.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1294,9 +1418,9 @@ mod tests {
|
||||
config_dir: Some("config".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[
|
||||
Want::Existing("home/.jjconfig.toml"),
|
||||
Want::New("config/jj/config.toml"),
|
||||
wants: vec![
|
||||
Want::existing("home/.jjconfig.toml"),
|
||||
Want::new("config/jj/config.toml"),
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -1309,7 +1433,7 @@ mod tests {
|
||||
config_dir: Some("config".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::Existing("config/jj/config.toml")],
|
||||
wants: vec![Want::existing("config/jj/config.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1320,7 +1444,7 @@ mod tests {
|
||||
config_dir: Some("config".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::New("config/jj/config.toml")],
|
||||
wants: vec![Want::new("config/jj/config.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1332,7 +1456,7 @@ mod tests {
|
||||
config_dir: Some("config".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::New("config/jj/config.toml")],
|
||||
wants: vec![Want::new("config/jj/config.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1343,7 +1467,7 @@ mod tests {
|
||||
jj_config: Some("custom.toml".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::Existing("custom.toml")],
|
||||
wants: vec![Want::existing("custom.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1354,7 +1478,7 @@ mod tests {
|
||||
jj_config: Some("custom.toml".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::New("custom.toml")],
|
||||
wants: vec![Want::new("custom.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1370,9 +1494,9 @@ mod tests {
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[
|
||||
Want::Existing("custom1.toml"),
|
||||
Want::Existing("custom2.toml"),
|
||||
wants: vec![
|
||||
Want::existing("custom1.toml"),
|
||||
Want::existing("custom2.toml"),
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -1389,7 +1513,7 @@ mod tests {
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::Existing("custom1.toml"), Want::New("custom2.toml")],
|
||||
wants: vec![Want::existing("custom1.toml"), Want::new("custom2.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1405,7 +1529,7 @@ mod tests {
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::Existing("custom1.toml"), Want::New("custom2.toml")],
|
||||
wants: vec![Want::existing("custom1.toml"), Want::new("custom2.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1416,7 +1540,7 @@ mod tests {
|
||||
jj_config: Some("".to_owned()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[],
|
||||
wants: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1428,7 +1552,7 @@ mod tests {
|
||||
config_dir: Some("config".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[Want::Existing("config/jj/config.toml")],
|
||||
wants: vec![Want::existing("config/jj/config.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1440,9 +1564,9 @@ mod tests {
|
||||
config_dir: Some("config".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[
|
||||
Want::Existing("home/.jjconfig.toml"),
|
||||
Want::New("config/jj/config.toml"),
|
||||
wants: vec![
|
||||
Want::existing("home/.jjconfig.toml"),
|
||||
Want::new("config/jj/config.toml"),
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -1455,9 +1579,9 @@ mod tests {
|
||||
config_dir: Some("config".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[
|
||||
Want::New("config/jj/config.toml"),
|
||||
Want::Existing("config/jj/conf.d"),
|
||||
wants: vec![
|
||||
Want::new("config/jj/config.toml"),
|
||||
Want::existing("config/jj/conf.d"),
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -1470,9 +1594,9 @@ mod tests {
|
||||
config_dir: Some("config".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: &[
|
||||
Want::Existing("config/jj/config.toml"),
|
||||
Want::Existing("config/jj/conf.d"),
|
||||
wants: vec![
|
||||
Want::existing("config/jj/config.toml"),
|
||||
Want::existing("config/jj/conf.d"),
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -1490,10 +1614,10 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
// Precedence order is important
|
||||
wants: &[
|
||||
Want::Existing("home/.jjconfig.toml"),
|
||||
Want::Existing("config/jj/config.toml"),
|
||||
Want::Existing("config/jj/conf.d"),
|
||||
wants: vec![
|
||||
Want::existing("home/.jjconfig.toml"),
|
||||
Want::existing("config/jj/config.toml"),
|
||||
Want::existing("config/jj/conf.d"),
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -1502,7 +1626,52 @@ mod tests {
|
||||
TestCase {
|
||||
files: &[],
|
||||
env: Default::default(),
|
||||
wants: &[],
|
||||
wants: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn config_path_macos_legacy_exists() -> TestCase {
|
||||
TestCase {
|
||||
files: &["macos-legacy/jj/config.toml"],
|
||||
env: UnresolvedConfigEnv {
|
||||
home_dir: Some("home".into()),
|
||||
config_dir: Some("config".into()),
|
||||
macos_legacy_config_dir: Some("macos-legacy".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: vec![
|
||||
Want::new("config/jj/config.toml"),
|
||||
Want::existing("macos-legacy/jj/config.toml"),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn config_path_macos_legacy_both_exist() -> TestCase {
|
||||
TestCase {
|
||||
files: &["macos-legacy/jj/config.toml", "config/jj/config.toml"],
|
||||
env: UnresolvedConfigEnv {
|
||||
home_dir: Some("home".into()),
|
||||
config_dir: Some("config".into()),
|
||||
macos_legacy_config_dir: Some("macos-legacy".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: vec![
|
||||
Want::existing("config/jj/config.toml"),
|
||||
Want::existing("macos-legacy/jj/config.toml"),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn config_path_macos_legacy_new() -> TestCase {
|
||||
TestCase {
|
||||
files: &[],
|
||||
env: UnresolvedConfigEnv {
|
||||
home_dir: Some("home".into()),
|
||||
config_dir: Some("config".into()),
|
||||
macos_legacy_config_dir: Some("macos-legacy".into()),
|
||||
..Default::default()
|
||||
},
|
||||
wants: vec![Want::new("config/jj/config.toml")],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1524,31 +1693,30 @@ mod tests {
|
||||
#[test_case(config_path_platform_existing_conf_dir_existing())]
|
||||
#[test_case(config_path_all_existing())]
|
||||
#[test_case(config_path_none())]
|
||||
#[test_case(config_path_macos_legacy_exists())]
|
||||
#[test_case(config_path_macos_legacy_both_exist())]
|
||||
#[test_case(config_path_macos_legacy_new())]
|
||||
fn test_config_path(case: TestCase) {
|
||||
let tmp = setup_config_fs(case.files);
|
||||
let env = resolve_config_env(&case.env, tmp.path());
|
||||
|
||||
let expected_existing: Vec<PathBuf> = case
|
||||
let all_expected_paths = case
|
||||
.wants
|
||||
.iter()
|
||||
.filter_map(|want| match want {
|
||||
Want::Existing(path) => Some(tmp.path().join(path)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(
|
||||
env.existing_user_config_paths().collect_vec(),
|
||||
expected_existing
|
||||
);
|
||||
.map(|w| w.rooted_path(tmp.path()))
|
||||
.collect_vec();
|
||||
let exists_expected_paths = case
|
||||
.wants
|
||||
.iter()
|
||||
.filter(|w| w.exists())
|
||||
.map(|w| w.rooted_path(tmp.path()))
|
||||
.collect_vec();
|
||||
|
||||
let expected_paths: Vec<PathBuf> = case
|
||||
.wants
|
||||
.iter()
|
||||
.map(|want| match want {
|
||||
Want::New(path) | Want::Existing(path) => tmp.path().join(path),
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(env.user_config_paths().collect_vec(), expected_paths);
|
||||
let all_paths = env.user_config_paths().collect_vec();
|
||||
let exists_paths = env.existing_user_config_paths().collect_vec();
|
||||
|
||||
assert_eq!(all_paths, all_expected_paths);
|
||||
assert_eq!(exists_paths, exists_expected_paths);
|
||||
}
|
||||
|
||||
fn setup_config_fs(files: &[&str]) -> tempfile::TempDir {
|
||||
@ -1567,6 +1735,7 @@ mod tests {
|
||||
let home_dir = env.home_dir.as_ref().map(|p| root.join(p));
|
||||
let env = UnresolvedConfigEnv {
|
||||
config_dir: env.config_dir.as_ref().map(|p| root.join(p)),
|
||||
macos_legacy_config_dir: env.macos_legacy_config_dir.as_ref().map(|p| root.join(p)),
|
||||
home_dir: home_dir.clone(),
|
||||
jj_config: env.jj_config.as_ref().map(|p| {
|
||||
join_paths(split_paths(p).map(|p| {
|
||||
@ -1583,7 +1752,7 @@ mod tests {
|
||||
ConfigEnv {
|
||||
home_dir,
|
||||
repo_path: None,
|
||||
user_config_paths: env.resolve(),
|
||||
user_config_paths: env.resolve(&Ui::null()),
|
||||
repo_config_path: None,
|
||||
command: None,
|
||||
}
|
||||
|
@ -153,8 +153,8 @@ fn pinentry_get_pw(url: &str) -> Option<String> {
|
||||
#[tracing::instrument]
|
||||
fn get_ssh_keys(_username: &str) -> Vec<PathBuf> {
|
||||
let mut paths = vec![];
|
||||
if let Some(home_dir) = dirs::home_dir() {
|
||||
let ssh_dir = Path::new(&home_dir).join(".ssh");
|
||||
if let Ok(home_dir) = etcetera::home_dir() {
|
||||
let ssh_dir = home_dir.join(".ssh");
|
||||
for filename in ["id_ed25519_sk", "id_ed25519", "id_rsa"] {
|
||||
let key_path = ssh_dir.join(filename);
|
||||
if key_path.is_file() {
|
||||
|
@ -1599,11 +1599,15 @@ The files in the `conf.d` directory are loaded in lexicographic order. This allo
|
||||
configs to be split across multiple files and combines well
|
||||
with [Conditional Variables](#conditional-variables).
|
||||
|
||||
| Platform | Location of `<PLATFORM_SPECIFIC>` dir | Example config file location |
|
||||
| :------- | :------------------------------------ | :-------------------------------------------------------- |
|
||||
| Linux | `$XDG_CONFIG_HOME` or `$HOME/.config` | `/home/alice/.config/jj/config.toml` |
|
||||
| macOS | `$HOME/Library/Application Support` | `/Users/Alice/Library/Application Support/jj/config.toml` |
|
||||
| Windows | `{FOLDERID_RoamingAppData}` | `C:\Users\Alice\AppData\Roaming\jj\config.toml` |
|
||||
| Platform | Location of `<PLATFORM_SPECIFIC>` dir | Example config file location |
|
||||
| :-------------- | :------------------------------------ | :-------------------------------------------------------- |
|
||||
| Linux and macOS | `$XDG_CONFIG_HOME` or `$HOME/.config` | `/home/alice/.config/jj/config.toml` |
|
||||
| Windows | `{FOLDERID_RoamingAppData}` | `C:\Users\Alice\AppData\Roaming\jj\config.toml` |
|
||||
|
||||
On macOS, jj used to put the user config in `~/Library/Application Support`,
|
||||
and jj will still look there for backwards compatibility purposes; this is
|
||||
considered a deprecated location, and you should use the new default
|
||||
`XDG_CONFIG_HOME`.
|
||||
|
||||
The location of the `jj` user config files/directories can also be overridden with the
|
||||
`JJ_CONFIG` environment variable. If it is not empty, it will be used instead
|
||||
|
Loading…
x
Reference in New Issue
Block a user