jj_cli/commands/git/
mod.rs

1// Copyright 2020-2023 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
15mod clone;
16mod colocation;
17mod export;
18mod fetch;
19mod import;
20mod init;
21mod push;
22mod remote;
23mod root;
24
25use std::io::Write as _;
26use std::path::Path;
27
28use clap::Subcommand;
29use clap::ValueEnum;
30use jj_lib::config::ConfigFile;
31use jj_lib::config::ConfigSource;
32use jj_lib::git;
33use jj_lib::git::UnexpectedGitBackendError;
34use jj_lib::ref_name::RemoteNameBuf;
35use jj_lib::ref_name::RemoteRefSymbol;
36use jj_lib::store::Store;
37
38use self::clone::GitCloneArgs;
39use self::clone::cmd_git_clone;
40use self::colocation::GitColocationCommand;
41use self::colocation::cmd_git_colocation;
42use self::export::GitExportArgs;
43use self::export::cmd_git_export;
44use self::fetch::GitFetchArgs;
45use self::fetch::cmd_git_fetch;
46use self::import::GitImportArgs;
47use self::import::cmd_git_import;
48use self::init::GitInitArgs;
49use self::init::cmd_git_init;
50use self::push::GitPushArgs;
51use self::push::cmd_git_push;
52pub use self::push::is_push_operation;
53use self::remote::RemoteCommand;
54use self::remote::cmd_git_remote;
55use self::root::GitRootArgs;
56use self::root::cmd_git_root;
57use crate::cli_util::CommandHelper;
58use crate::cli_util::WorkspaceCommandHelper;
59use crate::command_error::CommandError;
60use crate::command_error::user_error_with_message;
61use crate::ui::Ui;
62
63/// Commands for working with Git remotes and the underlying Git repo
64///
65/// See this [comparison], including a [table of commands].
66///
67/// [comparison]:
68///     https://docs.jj-vcs.dev/latest/git-comparison/.
69///
70/// [table of commands]:
71///     https://docs.jj-vcs.dev/latest/git-command-table
72#[derive(Subcommand, Clone, Debug)]
73pub enum GitCommand {
74    Clone(GitCloneArgs),
75    #[command(subcommand)]
76    Colocation(GitColocationCommand),
77    Export(GitExportArgs),
78    Fetch(GitFetchArgs),
79    Import(GitImportArgs),
80    Init(GitInitArgs),
81    Push(GitPushArgs),
82    #[command(subcommand)]
83    Remote(RemoteCommand),
84    Root(GitRootArgs),
85}
86
87pub fn cmd_git(
88    ui: &mut Ui,
89    command: &CommandHelper,
90    subcommand: &GitCommand,
91) -> Result<(), CommandError> {
92    match subcommand {
93        GitCommand::Clone(args) => cmd_git_clone(ui, command, args),
94        GitCommand::Colocation(subcommand) => cmd_git_colocation(ui, command, subcommand),
95        GitCommand::Export(args) => cmd_git_export(ui, command, args),
96        GitCommand::Fetch(args) => cmd_git_fetch(ui, command, args),
97        GitCommand::Import(args) => cmd_git_import(ui, command, args),
98        GitCommand::Init(args) => cmd_git_init(ui, command, args),
99        GitCommand::Push(args) => cmd_git_push(ui, command, args),
100        GitCommand::Remote(args) => cmd_git_remote(ui, command, args),
101        GitCommand::Root(args) => cmd_git_root(ui, command, args),
102    }
103}
104
105pub fn maybe_add_gitignore(workspace_command: &WorkspaceCommandHelper) -> Result<(), CommandError> {
106    if workspace_command.working_copy_shared_with_git() {
107        std::fs::write(
108            workspace_command
109                .workspace_root()
110                .join(".jj")
111                .join(".gitignore"),
112            "/*\n",
113        )
114        .map_err(|e| user_error_with_message("Failed to write .jj/.gitignore file", e))
115    } else {
116        Ok(())
117    }
118}
119
120fn get_single_remote(store: &Store) -> Result<Option<RemoteNameBuf>, UnexpectedGitBackendError> {
121    let mut names = git::get_all_remote_names(store)?;
122    Ok(match names.len() {
123        1 => names.pop(),
124        _ => None,
125    })
126}
127
128/// Sets repository level `trunk()` alias to the specified remote symbol.
129fn write_repository_level_trunk_alias(
130    ui: &Ui,
131    repo_path: &Path,
132    symbol: RemoteRefSymbol<'_>,
133) -> Result<(), CommandError> {
134    let mut file = ConfigFile::load_or_empty(ConfigSource::Repo, repo_path.join("config.toml"))?;
135    file.set_value(["revset-aliases", "trunk()"], symbol.to_string())
136        .expect("initial repo config shouldn't have invalid values");
137    file.save()?;
138    writeln!(
139        ui.status(),
140        "Setting the revset alias `trunk()` to `{symbol}`",
141    )?;
142    Ok(())
143}
144
145#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
146enum FetchTagsMode {
147    /// Always fetch all tags
148    All,
149
150    /// Only fetch tags that point to objects that are already being
151    /// transmitted.
152    Included,
153
154    /// Do not fetch any tags
155    None,
156}
157
158impl FetchTagsMode {
159    fn as_fetch_tags(&self) -> gix::remote::fetch::Tags {
160        match self {
161            Self::All => gix::remote::fetch::Tags::All,
162            Self::Included => gix::remote::fetch::Tags::Included,
163            Self::None => gix::remote::fetch::Tags::None,
164        }
165    }
166}