Skip to content

Commit 819dff3

Browse files
committed
feat: add "Pull --recurse-submodules" context menu option
Only shown when the repo has a .gitmodules file. Detects submodules during status query and passes the flag through to the context menu.
1 parent 912aa25 commit 819dff3

File tree

5 files changed

+27
-7
lines changed

5 files changed

+27
-7
lines changed

src/action.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub(crate) enum Action {
4141
GitPush(usize),
4242
GitPull(usize),
4343
GitPullRebase(usize),
44+
GitPullSubmodules(usize),
4445
GitOpComplete {
4546
index: usize,
4647
message: String,

src/app.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,14 @@ impl App {
509509
self.git_graph.set_error(msg.clone());
510510
}
511511
Action::ShowContextMenu { index, row, col } => {
512-
let (ahead, behind) = self
512+
let (ahead, behind, has_submodules) = self
513513
.repo_list
514514
.repos
515515
.get(index)
516516
.and_then(|e| e.status.as_ref())
517-
.map(|s| (s.ahead, s.behind))
518-
.unwrap_or((0, 0));
519-
self.context_menu.show(index, col, row, ahead, behind);
517+
.map(|s| (s.ahead, s.behind, s.has_submodules))
518+
.unwrap_or((0, 0, false));
519+
self.context_menu.show(index, col, row, ahead, behind, has_submodules);
520520
}
521521
Action::HideContextMenu => {
522522
self.context_menu.hide();
@@ -530,7 +530,7 @@ impl App {
530530
let _ = std::io::stdout().flush();
531531
}
532532
}
533-
Action::GitPush(idx) | Action::GitPull(idx) | Action::GitPullRebase(idx) => {
533+
Action::GitPush(idx) | Action::GitPull(idx) | Action::GitPullRebase(idx) | Action::GitPullSubmodules(idx) => {
534534
if let Some(entry) = self.repo_list.repos.get_mut(idx) {
535535
let branch = entry
536536
.status
@@ -543,6 +543,9 @@ impl App {
543543
Action::GitPullRebase(_) => {
544544
vec!["pull".into(), "--rebase".into()]
545545
}
546+
Action::GitPullSubmodules(_) => {
547+
vec!["pull".into(), "--recurse-submodules".into()]
548+
}
546549
_ => unreachable!(),
547550
};
548551
// Add origin <branch> so pull/push works even without upstream config

src/components/context_menu.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enum MenuAction {
2020
Push,
2121
Pull,
2222
PullRebase,
23+
PullSubmodules,
2324
}
2425

2526
struct MenuItem {
@@ -50,7 +51,7 @@ impl ContextMenu {
5051
}
5152
}
5253

53-
pub fn show(&mut self, repo_index: usize, col: u16, row: u16, ahead: usize, behind: usize) {
54+
pub fn show(&mut self, repo_index: usize, col: u16, row: u16, ahead: usize, behind: usize, has_submodules: bool) {
5455
self.visible = true;
5556
self.repo_index = repo_index;
5657
self.position = (col, row);
@@ -90,6 +91,13 @@ impl ContextMenu {
9091
},
9192
];
9293

94+
if has_submodules {
95+
self.items.push(MenuItem {
96+
label: "Pull --recurse-subs".into(),
97+
action: MenuAction::PullSubmodules,
98+
});
99+
}
100+
93101
self.state.select(Some(0));
94102
}
95103

@@ -142,6 +150,7 @@ impl ContextMenu {
142150
MenuAction::Push => Action::GitPush(self.repo_index),
143151
MenuAction::Pull => Action::GitPull(self.repo_index),
144152
MenuAction::PullRebase => Action::GitPullRebase(self.repo_index),
153+
MenuAction::PullSubmodules => Action::GitPullSubmodules(self.repo_index),
145154
};
146155
self.hide();
147156
Some(action)
@@ -232,7 +241,7 @@ impl Component for ContextMenu {
232241
.map(|item| {
233242
let style = match item.action {
234243
MenuAction::Push => Style::default().fg(Color::Green),
235-
MenuAction::Pull | MenuAction::PullRebase => Style::default().fg(Color::Yellow),
244+
MenuAction::Pull | MenuAction::PullRebase | MenuAction::PullSubmodules => Style::default().fg(Color::Yellow),
236245
_ => Style::default(),
237246
};
238247
ListItem::new(Line::from(Span::styled(&item.label, style)))

src/components/repo_list.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ impl RepoList {
126126
behind: 0,
127127
is_dirty: false,
128128
worktrees: 0,
129+
has_submodules: false,
129130
},
130131
});
131132
}

src/git/status.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub(crate) struct RepoStatus {
1010
pub is_dirty: bool,
1111
/// Number of linked worktrees (excludes the main working tree)
1212
pub worktrees: usize,
13+
/// True when .gitmodules exists (repo uses submodules)
14+
pub has_submodules: bool,
1315
}
1416

1517
#[derive(Clone, Debug, PartialEq)]
@@ -110,13 +112,17 @@ fn query_status_inner(path: &Path, fetch: bool) -> color_eyre::Result<RepoStatus
110112
// Count linked worktrees (excludes the main working tree)
111113
let worktrees = repo.worktrees().map(|wt| wt.len()).unwrap_or(0);
112114

115+
// Detect submodules by checking for .gitmodules
116+
let has_submodules = path.join(".gitmodules").is_file();
117+
113118
Ok(RepoStatus {
114119
branch,
115120
files,
116121
ahead,
117122
behind,
118123
is_dirty,
119124
worktrees,
125+
has_submodules,
120126
})
121127
}
122128

0 commit comments

Comments
 (0)