Skip to content

Commit

Permalink
all implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
Pistonight committed Nov 25, 2023
1 parent 3492ff1 commit 70de906
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 90 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "magoo"
version = "0.0.4"
version = "0.1.0"
edition = "2021"
description = "A wrapper for git submodule that simplifies the workflows"
repository = "https://github.com/Pistonite/magoo"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
![License Badge](https://img.shields.io/github/license/Pistonite/magoo)
![Issue Badge](https://img.shields.io/github/issues/Pistonite/magoo)

**In Development. commands left are: update, remove**
**In Development. commands left are: remove**

This ![magoo](https://raw.githubusercontent.com/Pistonite/magoo/main/magoo.webp) is Magoo, he helps you manage git submodules with ease, like `npm` or `cargo`, but for submodules.

Expand Down Expand Up @@ -78,7 +78,7 @@ It also deletes submodules that are deleted by others (by running `status --fix

### Show submodule status
```bash
magoo status [--long] [--all] [--fix]
magoo status [--long] [--fix]
```
Shows everything ![magoo](https://raw.githubusercontent.com/Pistonite/magoo/main/magoo.webp) knows about submodules in the current repo.

Expand All @@ -91,7 +91,7 @@ The `--all` option can potentially find more residues.
### Update submodules

![magoo](https://raw.githubusercontent.com/Pistonite/magoo/main/magoo.webp) updates the submodule by fetching and checking out the latest updates from the remote, tracked by
the `BRANCH` specified when you add it.
the `BRANCH` specified when you added it.

- Update all submodules to the latest
```bash
Expand Down
10 changes: 4 additions & 6 deletions README.txtpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ TXTPP#include magoo.txt
![License Badge](https://img.shields.io/github/license/Pistonite/magoo)
![Issue Badge](https://img.shields.io/github/issues/Pistonite/magoo)

**In Development. commands left are: update, remove**

TXTPP#tag MAGOO
TXTPP#include magoo.txt
This MAGOO is Magoo, he helps you manage git submodules with ease, like `npm` or `cargo`, but for submodules.
Expand Down Expand Up @@ -106,7 +104,7 @@ It also deletes submodules that are deleted by others (by running `status --fix

### Show submodule status
```bash
magoo status [--long] [--all] [--fix]
magoo status [--long] [--fix]
```
TXTPP#tag MAGOO
TXTPP#include magoo.txt
Expand All @@ -125,7 +123,7 @@ TXTPP#tag MAGOO
TXTPP#include magoo.txt

MAGOO updates the submodule by fetching and checking out the latest updates from the remote, tracked by
the `BRANCH` specified when you add it.
the `BRANCH` specified when you added it.

- Update all submodules to the latest
```bash
Expand All @@ -143,12 +141,12 @@ the `BRANCH` specified when you add it.
### Remove submodules
TXTPP#tag MAGOO
TXTPP#include magoo.txt
MAGOO can remove a submodule with ease:
MAGOO will remove every trace of a submodule with a single command:
```bash
magoo remove NAME
```

TXTPP#tag MAGOO
TXTPP#include magoo.txt
Note: Newer versions of git lets you delete a submodule with `git rm`. However, it doesn't delete the content in
`.git/modules`. MAGOO deletes those.
`.git/modules`. MAGOO deletes those as well.
8 changes: 8 additions & 0 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,14 @@ impl GitContext {
Ok(())
}

/// Run `git add`
pub fn add(&self, path: &str) -> Result<(), GitError> {
let top_level_dir = self.top_level_dir()?.display().to_string();

self.run_git_command(&["-C", &top_level_dir, "add", path], false)?;
Ok(())
}

/// Runs `git submodule deinit [-- <path>]`. Path should be from top level
pub fn submodule_deinit(&self, path: Option<&str>, force: bool) -> Result<(), GitError> {
let top_level_dir = self.top_level_dir()?.display().to_string();
Expand Down
122 changes: 98 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
//!
//! let command = magoo::StatusCommand {
//! git: true,
//! all: false,
//! fix: false,
//! long: false,
//! options: PrintOptions {
Expand All @@ -46,14 +45,13 @@
//! // for assertion below only
//! use magoo::{Command, StatusCommand, PrintOptions};
//!
//! let magoo = Magoo::try_parse_from(["magoo", "--dir", "my/repo", "status", "--all", "--verbose"]).unwrap();
//! let magoo = Magoo::try_parse_from(["magoo", "--dir", "my/repo", "status", "--long", "--verbose"]).unwrap();
//!
//! assert_eq!(magoo, Magoo {
//! subcmd: Command::Status(StatusCommand {
//! git: false,
//! all: true,
//! fix: false,
//! long: false,
//! long: true,
//! options: PrintOptions {
//! verbose: true,
//! quiet: false,
Expand Down Expand Up @@ -136,8 +134,7 @@ impl Command {
Command::Status(cmd) => cmd.set_print_options(),
Command::Install(cmd) => cmd.set_print_options(),
Command::Update(cmd) => cmd.set_print_options(),
_ => todo!(),
// Command::Remove(cmd) => cmd.set_print_options(),
Command::Remove(cmd) => cmd.set_print_options(),
}
}

Expand All @@ -153,8 +150,9 @@ impl Command {
Command::Update(cmd) => {
cmd.run(dir)?;
}
_ => todo!(),
// Command::Remove(cmd) => cmd.run(dir),
Command::Remove(cmd) => {
cmd.run(dir)?;
}
}

Ok(())
Expand All @@ -173,12 +171,6 @@ pub struct StatusCommand {
#[cfg_attr(feature = "cli", clap(long, short))]
pub long: bool,

/// Show every trace of submodules found.
///
/// This includes modules found in `.git/modules`, but not in anywhere else.
#[cfg_attr(feature = "cli", clap(long, short))]
pub all: bool,

/// Fix the submodules to be in a consistent state. (CAUTION - you should never have to do this if you let magoo manage the submodules, be sure to read the details in `magoo status --help` before using!)
///
/// If any submodule appears to be broken (likely due to changing
Expand Down Expand Up @@ -212,7 +204,7 @@ impl StatusCommand {
return Ok(Status::default());
}

let mut status = Status::read_from(&context, self.all)?;
let mut status = Status::read_from(&context)?;
let mut flat_status = status.flattened_mut();
if flat_status.is_empty() {
println!("No submodules found");
Expand All @@ -231,10 +223,8 @@ impl StatusCommand {
format!(" --dir {dir}")
};

let all_switch = if self.all { " --all" } else { "" };

for submodule in &flat_status {
submodule.print(&context, &dir_switch, all_switch, self.long)?;
submodule.print(&context, &dir_switch, self.long)?;
}
Ok(status)
}
Expand Down Expand Up @@ -300,7 +290,7 @@ impl InstallCommand {
let context = GitContext::try_from(dir)?;
let _guard = context.lock()?;

let mut status = Status::read_from(&context, true)?;
let mut status = Status::read_from(&context)?;
for submodule in status.flattened_mut() {
submodule.fix(&context)?;
}
Expand Down Expand Up @@ -363,6 +353,7 @@ pub struct UpdateCommand {
#[cfg_attr(feature = "cli", clap(long))]
pub bypass: bool,

/// Print options
#[cfg_attr(feature = "cli", clap(flatten))]
pub options: PrintOptions,
}
Expand All @@ -381,7 +372,7 @@ impl UpdateCommand {
match &self.name {
Some(name) => {
println_verbose!("Updating submodule: {name}");
let status = Status::read_from(&context, false)?;
let status = Status::read_from(&context)?;
let submodule = match status.modules.get(name) {
Some(submodule) => submodule,
None => {
Expand All @@ -393,7 +384,7 @@ impl UpdateCommand {
if let Some(path) = submodule.path() {
if path == name {
println_hint!(" however, there is a submodule \"{other_name}\" with path \"{path}\"");
println_hint!(" if you meant this submodule, use `magoo update {other_name}`");
println_hint!(" if you meant to update this submodule, use `magoo update {other_name}`");
break;
}
}
Expand Down Expand Up @@ -462,11 +453,94 @@ pub struct RemoveCommand {
/// The name of the submodule to remove
pub name: String,

/// Whether to force the submodule to be removed
///
/// The submodule will be removed even if it has local changes. (`git submodule deinit -f`)
/// Force remove the submodule. Will delete any local changes to the submodule
#[cfg_attr(feature = "cli", clap(long, short))]
pub force: bool,

/// Pass the `--force` flag to `git submobule deinit`
///
/// Cannot be used together with `--force`, since `--force` skips de-initializing.
#[cfg_attr(feature = "cli", clap(long))]
#[cfg_attr(feature = "cli", arg(conflicts_with("force")))]
pub force_deinit: bool,

/// Print options
#[cfg_attr(feature = "cli", clap(flatten))]
pub options: PrintOptions,
}

impl RemoveCommand {
/// Apply the print options
pub fn set_print_options(&self) {
self.options.apply();
}

/// Run the command in the given directory
pub fn run(&self, dir: &str) -> Result<(), GitError> {
let context = GitContext::try_from(dir)?;
let _guard = context.lock()?;

let name = &self.name;

println_verbose!("Removing submodule: {name}");
let mut status = Status::read_from(&context)?;
let submodule = match status.modules.get_mut(name) {
Some(submodule) => submodule,
None => {
println_error!("Submodule `{name}` not found!");
// maybe user passed in path instead of name?
println_verbose!("Trying to search for a path matching `{name}`");
for submodule in status.flattened() {
if let Some(other_name) = submodule.name() {
if let Some(path) = submodule.path() {
if path == name {
println_hint!(" however, there is a submodule \"{other_name}\" with path \"{path}\"");
println_hint!(" if you meant to remove this submodule, use `magoo remove {other_name}`");
break;
}
}
}
}

return Err(GitError::NeedFix(false));
}
};

if self.force {
println_verbose!("Removing (force): {name}");
submodule.force_delete(&context)?;
} else {
let path = match submodule.path() {
Some(x) => x,
None => {
println_error!("Submodule `{name}` does not have a path!");
println_hint!(" run `magoo status` to investigate.");
println_hint!(" if you are unsure of the problem, try hard removing the submodule with `magoo remove {name} --force`");
return Err(GitError::NeedFix(false));
}
};
if let Err(e) = context.submodule_deinit(Some(path), self.force_deinit) {
println_error!("Failed to deinitialize submodule `{name}`: {e}");
println_hint!(
" try running with `--force-deinit` to force deinitialize the module"
);
println_hint!(
" alternatively, running with `--force` will remove the module anyway."
);
return Err(GitError::NeedFix(false));
}

submodule.force_remove_module_dir(&context)?;
submodule.force_remove_config(&context)?;
submodule.force_remove_from_dot_gitmodules(&context)?;
submodule.force_remove_from_index(&context)?;
}

println_info!();
println_info!("Submodules removed successfully.");
println_hint!(" run `git status` to check the changes");
Ok(())
}
}

/// Printing options for all commands
Expand Down
39 changes: 11 additions & 28 deletions src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,13 @@ impl Status {
}

/// Get the submodule status in the repository.
///
/// If `all` is false, it will not include submodules that are only in the index and in
/// `.git/modules`
pub fn read_from(context: &GitContext, all: bool) -> Result<Self, GitError> {
pub fn read_from(context: &GitContext) -> Result<Self, GitError> {
let mut status = Self::default();
status.read_dot_gitmodules(context)?;
status.read_dot_git_config(context)?;
// read .git/modules
if all {
status.find_all_git_modules(context)?;
} else {
for (name, submodule) in status.modules.iter_mut() {
if let Ok(module) = Self::read_git_module(name, context) {
submodule.in_modules = Some(module);
}
}
};
status.read_submodules_in_index(context, all)?;
status.find_all_git_modules(context)?;
status.read_submodules_in_index(context)?;

Ok(status)
}
Expand Down Expand Up @@ -337,11 +326,7 @@ impl Status {
}

/// Use `git ls-files` to list submodules stored in the index into self
fn read_submodules_in_index(
&mut self,
context: &GitContext,
all: bool,
) -> Result<(), GitError> {
fn read_submodules_in_index(&mut self, context: &GitContext) -> Result<(), GitError> {
let index_list = context.ls_files(&[r#"--format=%(objectmode) %(objectname) %(path)"#])?;

let mut path_to_index_object = BTreeMap::new();
Expand Down Expand Up @@ -387,15 +372,13 @@ impl Status {
}
}

if all {
for index_obj in path_to_index_object.into_values() {
self.nameless.push(Submodule {
in_gitmodules: None,
in_config: None,
in_index: Some(index_obj),
in_modules: None,
});
}
for index_obj in path_to_index_object.into_values() {
self.nameless.push(Submodule {
in_gitmodules: None,
in_config: None,
in_index: Some(index_obj),
in_modules: None,
});
}
Ok(())
}
Expand Down
Loading

0 comments on commit 70de906

Please sign in to comment.