Skip to content
1 change: 0 additions & 1 deletion src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@ pub enum Commands {
name: String,
},

// TODO: Implement this command (use git2). Functionally this changes a module to `active = false` in our config and `.gitmodules`, but does not delete the submodule from the filesystem.
#[command(
name = "disable",
visible_alias = "d",
Expand Down
11 changes: 11 additions & 0 deletions src/git_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,17 @@ impl GitManager {
.submodules
.update_entry(name.to_string(), updated);

// Update .gitmodules
if let Ok(mut entries) = self.git_ops.read_gitmodules() {
if let Some(mut gitmodules_entry) = entries.get_submodule(name).cloned() {
gitmodules_entry.active = Some(false);
entries.update_entry(name.to_string(), gitmodules_entry);
if let Err(e) = self.git_ops.write_gitmodules(&entries) {
eprintln!("Warning: Failed to update .gitmodules: {e}");
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This .gitmodules update looks up the entry by name, but .gitmodules sections are not guaranteed to use the same identifier as your config key (e.g., name vs path). If entry.path differs from name, this won’t update the intended .gitmodules section. Consider locating the .gitmodules entry by matching entry.path (similar to other path-based lookups in the codebase) and then updating that section’s active flag.

Suggested change
if let Some(mut gitmodules_entry) = entries.get_submodule(name).cloned() {
gitmodules_entry.active = Some(false);
entries.update_entry(name.to_string(), gitmodules_entry);
if let Err(e) = self.git_ops.write_gitmodules(&entries) {
eprintln!("Warning: Failed to update .gitmodules: {e}");
let gitmodules_key = entries
.submodules
.iter()
.find(|(_, submodule)| submodule.path.as_deref() == Some(path.as_str()))
.map(|(key, _)| key.clone());
if let Some(gitmodules_key) = gitmodules_key {
if let Some(mut gitmodules_entry) = entries.get_submodule(&gitmodules_key).cloned() {
gitmodules_entry.active = Some(false);
entries.update_entry(gitmodules_key, gitmodules_entry);
if let Err(e) = self.git_ops.write_gitmodules(&entries) {
eprintln!("Warning: Failed to update .gitmodules: {e}");
}

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new behavior of writing active = false into .gitmodules isn’t covered by existing disable tests (they only assert submod.toml changes). Add an integration/contract assertion that .gitmodules contains submodule.<section>.active = false after disable, ideally including a case where submodule name != path.

Suggested change
if let Some(mut gitmodules_entry) = entries.get_submodule(name).cloned() {
gitmodules_entry.active = Some(false);
entries.update_entry(name.to_string(), gitmodules_entry);
if let Err(e) = self.git_ops.write_gitmodules(&entries) {
eprintln!("Warning: Failed to update .gitmodules: {e}");
let gitmodules_key = if entries.get_submodule(name).is_some() {
Some(name.to_string())
} else if entries.get_submodule(&path).is_some() {
Some(path.clone())
} else {
None
};
if let Some(gitmodules_key) = gitmodules_key {
if let Some(mut gitmodules_entry) = entries.get_submodule(&gitmodules_key).cloned() {
gitmodules_entry.active = Some(false);
entries.update_entry(gitmodules_key, gitmodules_entry);
if let Err(e) = self.git_ops.write_gitmodules(&entries) {
eprintln!("Warning: Failed to update .gitmodules: {e}");
}

Copilot uses AI. Check for mistakes.
}
}
}

self.write_full_config()?;
println!("Disabled submodule '{name}'.");
Ok(())
Expand Down
4 changes: 4 additions & 0 deletions src/git_ops/git2_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ impl GitOperations for Git2Operations {
};
config.set_str(&format!("submodule.{name}.update"), update_str)?;
}
if let Some(active) = entry.active {
let active_str = if active { "true" } else { "false" };
config.set_str(&format!("submodule.{name}.active"), active_str)?;
}
Comment on lines +249 to +252
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Git2Operations::write_gitmodules is expected to update the .gitmodules file, but this block writes submodule.*.active into self.repo.config() (i.e., .git/config). In git2-fallback scenarios, disable can report success without actually changing .gitmodules as described in the PR. Consider writing active via a config opened on the .gitmodules path (or git config -f .gitmodules ...) and ensure the section key matches the submodule’s actual config key (git2 frequently keys by path when name != path).

Copilot uses AI. Check for mistakes.
// Set URL if different
if let Some(url) = &entry.url
&& submodule.url() != Some(url.as_str()) {
Expand Down
9 changes: 9 additions & 0 deletions src/git_ops/gix_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ impl GitOperations for GixOperations {
value.as_bytes().as_bstr(),
)?;
}
if let Some(active) = entry.active {
let value = if active { "true" } else { "false" };
git_config.set_raw_value_by(
"submodule",
Some(subsection_name),
"active",
value.as_bytes().as_bstr(),
)?;
}
}

// Write to .gitmodules file
Expand Down
Loading