git: add a hidden submodule subcommand

Add the `submodule` subcommand, which will remain hidden while we are
polishing up the submodules feature. Also, add a debugging-only
sub-subcommand `print-gitmodules` that tests our .gitmodules parser with
the .gitmodules in the working copy.
This commit is contained in:
Glen Choo 2023-04-03 16:28:19 -07:00
parent 7afaa2487b
commit 777b786c53
3 changed files with 128 additions and 3 deletions

View file

@ -31,7 +31,7 @@ use indexmap::IndexSet;
use itertools::Itertools;
use jujutsu_lib::backend::{BackendError, ChangeId, CommitId, ObjectId, TreeId};
use jujutsu_lib::commit::Commit;
use jujutsu_lib::git::{GitExportError, GitImportError};
use jujutsu_lib::git::{GitConfigParseError, GitExportError, GitImportError};
use jujutsu_lib::git_backend::GitBackend;
use jujutsu_lib::gitignore::GitIgnoreFile;
use jujutsu_lib::hex_util::to_reverse_hex;
@ -325,6 +325,12 @@ impl From<clap::Error> for CommandError {
}
}
impl From<GitConfigParseError> for CommandError {
fn from(err: GitConfigParseError) -> Self {
CommandError::InternalError(format!("Failed to parse Git config: {err} "))
}
}
/// Handle to initialize or change tracing subscription.
#[derive(Clone, Debug)]
pub struct TracingSubscription {

View file

@ -8,12 +8,13 @@ use std::time::Instant;
use clap::{ArgGroup, Subcommand};
use itertools::Itertools;
use jujutsu_lib::backend::ObjectId;
use jujutsu_lib::git::{self, GitFetchError, GitPushError, GitRefUpdate};
use jujutsu_lib::backend::{ObjectId, TreeValue};
use jujutsu_lib::git::{self, parse_gitmodules, GitFetchError, GitPushError, GitRefUpdate};
use jujutsu_lib::git_backend::GitBackend;
use jujutsu_lib::op_store::{BranchTarget, RefTarget};
use jujutsu_lib::refs::{classify_branch_push_action, BranchPushAction, BranchPushUpdate};
use jujutsu_lib::repo::Repo;
use jujutsu_lib::repo_path::RepoPath;
use jujutsu_lib::revset::{self, RevsetIteratorExt as _};
use jujutsu_lib::settings::{ConfigResultExt as _, UserSettings};
use jujutsu_lib::store::Store;
@ -43,6 +44,8 @@ pub enum GitCommands {
Push(GitPushArgs),
Import(GitImportArgs),
Export(GitExportArgs),
#[command(subcommand, hide = true)]
Submodule(GitSubmoduleCommands),
}
/// Manage Git remotes
@ -155,6 +158,23 @@ pub struct GitImportArgs {}
#[derive(clap::Args, Clone, Debug)]
pub struct GitExportArgs {}
/// FOR INTERNAL USE ONLY Interact with git submodules
#[derive(Subcommand, Clone, Debug)]
pub enum GitSubmoduleCommands {
/// Print the relevant contents from .gitmodules. For debugging purposes
/// only.
PrintGitmodules(GitSubmodulePrintGitmodulesArgs),
}
/// Print debugging info about Git submodules
#[derive(clap::Args, Clone, Debug)]
#[command(hide = true)]
pub struct GitSubmodulePrintGitmodulesArgs {
/// Read .gitmodules from the given revision.
#[arg(long, short = 'r', default_value = "@")]
revisions: RevisionArg,
}
fn get_git_repo(store: &Store) -> Result<git2::Repository, CommandError> {
match store.backend_impl().downcast_ref::<GitBackend>() {
None => Err(user_error("The repo is not backed by a git repo")),
@ -1016,6 +1036,37 @@ fn cmd_git_export(
Ok(())
}
fn cmd_git_submodule_print_gitmodules(
ui: &mut Ui,
command: &CommandHelper,
args: &GitSubmodulePrintGitmodulesArgs,
) -> Result<(), CommandError> {
let workspace_command = command.workspace_helper(ui)?;
let repo = workspace_command.repo();
let commit = workspace_command.resolve_single_rev(&args.revisions)?;
let gitmodules_path = RepoPath::from_internal_string(".gitmodules");
let mut gitmodules_file = match commit.tree().path_value(&gitmodules_path) {
None => {
writeln!(ui, "No submodules!")?;
return Ok(());
}
Some(TreeValue::File { id, .. }) => repo.store().read_file(&gitmodules_path, &id)?,
_ => {
return Err(user_error(".gitmodules is not a file."));
}
};
let submodules = parse_gitmodules(&mut gitmodules_file)?;
for (name, submodule) in submodules {
writeln!(
ui,
"name:{}\nurl:{}\npath:{}\n\n",
name, submodule.url, submodule.path
)?;
}
Ok(())
}
pub fn cmd_git(
ui: &mut Ui,
command: &CommandHelper,
@ -1039,5 +1090,8 @@ pub fn cmd_git(
GitCommands::Push(command_matches) => cmd_git_push(ui, command, command_matches),
GitCommands::Import(command_matches) => cmd_git_import(ui, command, command_matches),
GitCommands::Export(command_matches) => cmd_git_export(ui, command, command_matches),
GitCommands::Submodule(GitSubmoduleCommands::PrintGitmodules(command_matches)) => {
cmd_git_submodule_print_gitmodules(ui, command, command_matches)
}
}
}

View file

@ -0,0 +1,65 @@
// Copyright 2020 The Jujutsu Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::common::TestEnvironment;
pub mod common;
#[test]
fn test_gitsubmodule_print_gitmodules() {
let test_env = TestEnvironment::default();
let workspace_root = test_env.env_root().join("repo");
git2::Repository::init(&workspace_root).unwrap();
test_env.jj_cmd_success(&workspace_root, &["init", "--git-repo", "."]);
std::fs::write(
workspace_root.join(".gitmodules"),
"
[submodule \"old\"]
path = old
url = https://github.com/old/old.git
",
)
.unwrap();
test_env.jj_cmd_success(&workspace_root, &["new"]);
std::fs::write(
workspace_root.join(".gitmodules"),
"
[submodule \"new\"]
path = new
url = https://github.com/new/new.git
",
)
.unwrap();
let stdout = test_env.jj_cmd_success(
&workspace_root,
&["git", "submodule", "print-gitmodules", "-r", "@-"],
);
insta::assert_snapshot!(stdout, @r###"
name:old
url:https://github.com/old/old.git
path:old
"###);
let stdout =
test_env.jj_cmd_success(&workspace_root, &["git", "submodule", "print-gitmodules"]);
insta::assert_snapshot!(stdout, @r###"
name:new
url:https://github.com/new/new.git
path:new
"###);
}