From 986d34a5acd520fbec91386675bec8013affc6bd Mon Sep 17 00:00:00 2001
From: extrawurst <776816+extrawurst@users.noreply.github.com>
Date: Wed, 31 Aug 2022 10:51:08 +0200
Subject: [PATCH] support opening submodule (#1298)
---
Cargo.lock | 20 +++++
README.md | 1 +
asyncgit/Cargo.toml | 1 +
asyncgit/src/error.rs | 9 +-
asyncgit/src/sync/mod.rs | 17 +++-
asyncgit/src/sync/repository.rs | 2 +-
asyncgit/src/sync/submodules.rs | 155 ++++++++++++++++++++++++++++----
src/app.rs | 37 ++++++--
src/components/submodules.rs | 110 +++++++++++++++++++++--
src/input.rs | 1 +
src/keys/key_config.rs | 2 +-
src/keys/key_list.rs | 4 +-
src/keys/key_list_file.rs | 1 +
src/keys/symbols.rs | 2 +-
src/main.rs | 46 ++++++++--
src/queue.rs | 2 +
src/strings.rs | 27 ++++++
src/ui/style.rs | 6 +-
18 files changed, 392 insertions(+), 51 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 12d9652c..213f8cfa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -67,6 +67,7 @@ version = "0.21.0"
dependencies = [
"crossbeam-channel",
"easy-cast",
+ "env_logger",
"git2",
"invalidstring",
"log",
@@ -422,6 +423,19 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+[[package]]
+name = "env_logger"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
[[package]]
name = "fancy-regex"
version = "0.7.1"
@@ -692,6 +706,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
[[package]]
name = "iana-time-zone"
version = "0.1.46"
diff --git a/README.md b/README.md
index f0a7a032..93740ef3 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@
- Browse commit log, diff committed changes
- Scalable terminal UI layout
- Async git API for fluid control
+- Submodule support
## 2. Motivation [Top ▲](#table-of-contents)
diff --git a/asyncgit/Cargo.toml b/asyncgit/Cargo.toml
index 95ec983b..b2ebd66f 100644
--- a/asyncgit/Cargo.toml
+++ b/asyncgit/Cargo.toml
@@ -28,6 +28,7 @@ unicode-truncate = "0.2.0"
url = "2.2"
[dev-dependencies]
+env_logger = "0.9"
invalidstring = { path = "../invalidstring", version = "0.1" }
pretty_assertions = "1.3"
serial_test = "0.9"
diff --git a/asyncgit/src/error.rs b/asyncgit/src/error.rs
index e733284d..5cbed38b 100644
--- a/asyncgit/src/error.rs
+++ b/asyncgit/src/error.rs
@@ -1,6 +1,9 @@
#![allow(renamed_and_removed_lints, clippy::unknown_clippy_lints)]
-use std::{num::TryFromIntError, string::FromUtf8Error};
+use std::{
+ num::TryFromIntError, path::StripPrefixError,
+ string::FromUtf8Error,
+};
use thiserror::Error;
///
@@ -50,6 +53,10 @@ pub enum Error {
#[error("git error:{0}")]
Git(#[from] git2::Error),
+ ///
+ #[error("strip prefix error: {0}")]
+ StripPrefix(#[from] StripPrefixError),
+
///
#[error("utf8 error:{0}")]
Utf8Conversion(#[from] FromUtf8Error),
diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs
index 53778a8e..7a72b69f 100644
--- a/asyncgit/src/sync/mod.rs
+++ b/asyncgit/src/sync/mod.rs
@@ -82,7 +82,8 @@ pub use stash::{
pub use state::{repo_state, RepoState};
pub use status::is_workdir_clean;
pub use submodules::{
- get_submodules, update_submodule, SubmoduleInfo, SubmoduleStatus,
+ get_submodules, submodule_parent_info, update_submodule,
+ SubmoduleInfo, SubmoduleParentInfo, SubmoduleStatus,
};
pub use tags::{
delete_tag, get_tags, get_tags_with_metadata, CommitTags, Tag,
@@ -209,6 +210,8 @@ mod tests {
///
pub fn repo_init_empty() -> Result<(TempDir, Repository)> {
+ init_log();
+
sandbox_config_files();
let td = TempDir::new()?;
@@ -223,6 +226,8 @@ mod tests {
///
pub fn repo_init() -> Result<(TempDir, Repository)> {
+ init_log();
+
sandbox_config_files();
let td = TempDir::new()?;
@@ -266,8 +271,18 @@ mod tests {
Ok((td, repo))
}
+ // init log
+ fn init_log() {
+ let _ = env_logger::builder()
+ .is_test(true)
+ .filter_level(log::LevelFilter::Trace)
+ .try_init();
+ }
+
/// Same as repo_init, but the repo is a bare repo (--bare)
pub fn repo_init_bare() -> Result<(TempDir, Repository)> {
+ init_log();
+
let tmp_repo_dir = TempDir::new()?;
let bare_repo = Repository::init_bare(tmp_repo_dir.path())?;
Ok((tmp_repo_dir, bare_repo))
diff --git a/asyncgit/src/sync/repository.rs b/asyncgit/src/sync/repository.rs
index 1fad1206..24620d4c 100644
--- a/asyncgit/src/sync/repository.rs
+++ b/asyncgit/src/sync/repository.rs
@@ -11,7 +11,7 @@ use crate::error::Result;
pub type RepoPathRef = RefCell;
///
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub enum RepoPath {
///
Path(PathBuf),
diff --git a/asyncgit/src/sync/submodules.rs b/asyncgit/src/sync/submodules.rs
index f81f9573..b32a67d8 100644
--- a/asyncgit/src/sync/submodules.rs
+++ b/asyncgit/src/sync/submodules.rs
@@ -1,15 +1,24 @@
-use std::path::PathBuf;
+//TODO:
+// #![allow(unused_variables, dead_code)]
-use git2::SubmoduleUpdateOptions;
+use std::path::{Path, PathBuf};
+
+use git2::{
+ Repository, RepositoryOpenFlags, Submodule,
+ SubmoduleUpdateOptions,
+};
use scopetime::scope_time;
use super::{repo, CommitId, RepoPath};
-use crate::{error::Result, Error};
+use crate::{error::Result, sync::utils::work_dir, Error};
pub use git2::SubmoduleStatus;
///
+#[derive(Debug)]
pub struct SubmoduleInfo {
+ ///
+ pub name: String,
///
pub path: PathBuf,
///
@@ -22,6 +31,17 @@ pub struct SubmoduleInfo {
pub status: SubmoduleStatus,
}
+///
+#[derive(Debug)]
+pub struct SubmoduleParentInfo {
+ /// where to find parent repo
+ pub parent_gitpath: PathBuf,
+ /// where to find submodule git path
+ pub submodule_gitpath: PathBuf,
+ /// `submodule_info` from perspective of parent repo
+ pub submodule_info: SubmoduleInfo,
+}
+
impl SubmoduleInfo {
///
pub fn get_repo_path(
@@ -35,6 +55,24 @@ impl SubmoduleInfo {
}
}
+fn submodule_to_info(s: &Submodule, r: &Repository) -> SubmoduleInfo {
+ let status = r
+ .submodule_status(
+ s.name().unwrap_or_default(),
+ git2::SubmoduleIgnore::None,
+ )
+ .unwrap_or(SubmoduleStatus::empty());
+
+ SubmoduleInfo {
+ name: s.name().unwrap_or_default().into(),
+ path: s.path().to_path_buf(),
+ id: s.workdir_id().map(CommitId::from),
+ head_id: s.head_id().map(CommitId::from),
+ url: s.url().map(String::from),
+ status,
+ }
+}
+
///
pub fn get_submodules(
repo_path: &RepoPath,
@@ -46,22 +84,7 @@ pub fn get_submodules(
let res = r
.submodules()?
.iter()
- .map(|s| {
- let status = repo2
- .submodule_status(
- s.name().unwrap_or_default(),
- git2::SubmoduleIgnore::None,
- )
- .unwrap_or(SubmoduleStatus::empty());
-
- SubmoduleInfo {
- path: s.path().to_path_buf(),
- id: s.workdir_id().map(CommitId::from),
- head_id: s.head_id().map(CommitId::from),
- url: s.url().map(String::from),
- status,
- }
- })
+ .map(|s| submodule_to_info(s, &repo2))
.collect();
Ok(res)
@@ -82,3 +105,97 @@ pub fn update_submodule(
Ok(())
}
+
+/// query whether `repo_path` points to a repo that is part of a parent git which contains it as a submodule
+pub fn submodule_parent_info(
+ repo_path: &RepoPath,
+) -> Result