Skip to content

Commit 985ff83

Browse files
committed
Add feature to update only tracked files (gitui-org#933)
1 parent 271d3f6 commit 985ff83

File tree

7 files changed

+133
-9
lines changed

7 files changed

+133
-9
lines changed

CHANGELOG.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## Unreleased
9-
9+
- Add the 'u' shortcut to stage tracked files only [[@Revantus](https://github.com/Revantus)] ([#933](https://github.com/extrawurst/gitui/issues/933))
10+
1011
## Added
1112
- add `trace-libgit` feature to make git tracing optional [[@dm9pZCAq](https://github.com/dm9pZCAq)] ([#902](https://github.com/extrawurst/gitui/issues/902))
1213
- support merging and rebasing remote branches ([#920](https://github.com/extrawurst/gitui/issues/920))
@@ -121,7 +122,7 @@ see `vim_style_key_config.ron` for their default vim binding
121122
- indicator for longer commit message than displayed ([#773](https://github.com/extrawurst/gitui/issues/773))
122123

123124
![msg-len](assets/long-msg-indicator.gif)
124-
125+
125126
## Fixed
126127
- wrong file with same name shown in file tree ([#748](https://github.com/extrawurst/gitui/issues/748))
127128
- filetree collapsing broken on windows ([#761](https://github.com/extrawurst/gitui/issues/761))
@@ -157,7 +158,7 @@ see `vim_style_key_config.ron` for their default vim binding
157158
- `--bugreport` cmd line arg to help diagnostics [[@zcorniere](https://github.com/zcorniere)] ([#695](https://github.com/extrawurst/gitui/issues/695))
158159

159160
## Changed
160-
- smarter log timestamps ([#682](https://github.com/extrawurst/gitui/issues/682))
161+
- smarter log timestamps ([#682](https://github.com/extrawurst/gitui/issues/682))
161162
- create-branch popup aligned with rename-branch [[@bruceCoelho](https://github.com/bruceCoelho)] ([#679](https://github.com/extrawurst/gitui/issues/679))
162163
- smart focus change after staging all files ([#706](https://github.com/extrawurst/gitui/issues/706))
163164
- do not allow to commit when `gpgsign` enabled ([#740](https://github.com/extrawurst/gitui/issues/740))
@@ -244,13 +245,13 @@ Thanks for your interest and support over this year! Read more about the 1 year
244245
![chars-branch-name](assets/chars_and_branchname.gif)
245246

246247
### Breaking Change
247-
- MacOS config directory now uses `~/.config/gitui` [[@remique](https://github.com/remique)] ([#317](https://github.com/extrawurst/gitui/issues/317))
248+
- MacOS config directory now uses `~/.config/gitui` [[@remique](https://github.com/remique)] ([#317](https://github.com/extrawurst/gitui/issues/317))
248249

249250
### Added
250251
- support for pull (fetch + simple merging) ([#319](https://github.com/extrawurst/gitui/issues/319))
251252
- show used char count in input texts ([#466](https://github.com/extrawurst/gitui/issues/466))
252253
- support smoother left/right toggle/keys for commit details ([#418](https://github.com/extrawurst/gitui/issues/418))
253-
- support *force push* command [[@WizardOhio24](https://github.com/WizardOhio24)] ([#274](https://github.com/extrawurst/gitui/issues/274))
254+
- support *force push* command [[@WizardOhio24](https://github.com/WizardOhio24)] ([#274](https://github.com/extrawurst/gitui/issues/274))
254255

255256
### Fixed
256257
- don't close branchlist every time ([#550](https://github.com/extrawurst/gitui/issues/550))

asyncgit/src/sync/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ pub use tags::{
8181
pub use tree::{tree_file_content, tree_files, TreeFile};
8282
pub use utils::{
8383
get_head, get_head_tuple, is_bare_repo, is_repo, repo_dir,
84-
stage_add_all, stage_add_file, stage_addremoved, Head,
84+
stage_add_all, stage_add_file, stage_add_updated,
85+
stage_addremoved, Head,
8586
};
8687

8788
#[cfg(test)]

asyncgit/src/sync/utils.rs

+82-1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,40 @@ pub fn stage_add_all(repo_path: &str, pattern: &str) -> Result<()> {
149149
Ok(())
150150
}
151151

152+
/// add all changed files that are currently tracked
153+
pub fn stage_add_updated(
154+
repo_path: &str,
155+
pattern: &str,
156+
) -> Result<()> {
157+
scope_time!("stage_add_updated");
158+
159+
let repo = repo(repo_path)?;
160+
161+
let mut index = repo.index()?;
162+
163+
// Gets the status of a file in the index.
164+
// If it's in the working tree update it.
165+
// If for whatever reason we can't get the status of a file,
166+
// treat it as an empty status.
167+
let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
168+
let status = repo
169+
.status_file(path)
170+
.unwrap_or_else(|_| git2::Status::empty());
171+
172+
if status.contains(git2::Status::WT_MODIFIED) {
173+
0
174+
} else {
175+
1
176+
}
177+
};
178+
let cb = Some(cb as &mut git2::IndexMatchedPath);
179+
180+
index.update_all(vec![pattern], cb)?;
181+
index.write()?;
182+
183+
Ok(())
184+
}
185+
152186
/// Undo last commit in repo
153187
pub fn undo_last_commit(repo_path: &str) -> Result<()> {
154188
let repo = repo(repo_path)?;
@@ -229,7 +263,7 @@ mod tests {
229263
},
230264
};
231265
use std::{
232-
fs::{self, remove_file, File},
266+
fs::{self, remove_file, File, OpenOptions},
233267
io::Write,
234268
path::Path,
235269
};
@@ -271,6 +305,53 @@ mod tests {
271305
assert_eq!(get_statuses(repo_path), (1, 1));
272306
}
273307

308+
#[test]
309+
fn test_updated_only_tracked() -> Result<()> {
310+
let tracked_file = "file1.txt";
311+
let (_td, repo) = repo_init().unwrap();
312+
let root = repo.path().parent().unwrap();
313+
let repo_path = root.as_os_str().to_str().unwrap();
314+
315+
// Create and commit file 1
316+
write_commit_file(
317+
&repo,
318+
tracked_file,
319+
"test file1 content",
320+
"c1",
321+
);
322+
323+
// No changes in (wd, stage)
324+
assert_eq!(get_statuses(repo_path), (0, 0));
325+
326+
// Create new untracked file 2
327+
File::create(&root.join(Path::new("file2.txt")))
328+
.unwrap()
329+
.write_all(b"test file2 content")
330+
.unwrap();
331+
332+
// Single change in wd
333+
assert_eq!(get_statuses(repo_path), (1, 0));
334+
335+
// Add new content to file 1
336+
OpenOptions::new()
337+
.write(true)
338+
.append(true)
339+
.open(&root.join(Path::new(tracked_file)))
340+
.unwrap()
341+
.write_all(b"add new content")?;
342+
343+
// Two changes in wd, nothing staged
344+
assert_eq!(get_statuses(repo_path), (2, 0));
345+
346+
// Only add file 1 to the stage as file 2 is untracked
347+
stage_add_updated(repo_path, "*").unwrap();
348+
349+
// Single change in wd and stage
350+
assert_eq!(get_statuses(repo_path), (1, 1));
351+
352+
Ok(())
353+
}
354+
274355
#[test]
275356
fn test_staging_folder() -> Result<()> {
276357
let (_td, repo) = repo_init().unwrap();

src/components/changes.rs

+28
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ impl ChangesComponent {
120120
Ok(())
121121
}
122122

123+
fn index_add_updated(&mut self) -> Result<()> {
124+
sync::stage_add_updated(CWD, "*")?;
125+
126+
self.queue.push(InternalEvent::Update(NeedsUpdate::ALL));
127+
128+
Ok(())
129+
}
130+
123131
fn stage_remove_all(&mut self) -> Result<()> {
124132
sync::reset_stage(CWD, "*")?;
125133

@@ -195,6 +203,11 @@ impl Component for ChangesComponent {
195203
true,
196204
some_selection && self.focused(),
197205
));
206+
out.push(CommandInfo::new(
207+
strings::commands::stage_update(&self.key_config),
208+
true,
209+
some_selection && self.focused(),
210+
));
198211
out.push(CommandInfo::new(
199212
strings::commands::stage_item(&self.key_config),
200213
true,
@@ -259,6 +272,21 @@ impl Component for ChangesComponent {
259272
self.queue
260273
.push(InternalEvent::StatusLastFileMoved);
261274
Ok(EventState::Consumed)
275+
} else if e == self.key_config.status_stage_update
276+
&& !self.is_empty()
277+
{
278+
if self.is_working_dir {
279+
try_or_popup!(
280+
self,
281+
"staging all tracked error:",
282+
self.index_add_updated()
283+
);
284+
} else {
285+
self.stage_remove_all()?;
286+
}
287+
self.queue
288+
.push(InternalEvent::StatusLastFileMoved);
289+
Ok(EventState::Consumed)
262290
} else if e == self.key_config.status_reset_item
263291
&& self.is_working_dir
264292
{

src/keys.rs

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub struct KeyConfig {
5656
pub blame: KeyEvent,
5757
pub edit_file: KeyEvent,
5858
pub status_stage_all: KeyEvent,
59+
pub status_stage_update: KeyEvent,
5960
pub status_reset_item: KeyEvent,
6061
pub status_ignore_file: KeyEvent,
6162
pub diff_stage_lines: KeyEvent,
@@ -130,6 +131,7 @@ impl Default for KeyConfig {
130131
blame: KeyEvent { code: KeyCode::Char('B'), modifiers: KeyModifiers::SHIFT},
131132
edit_file: KeyEvent { code: KeyCode::Char('e'), modifiers: KeyModifiers::empty()},
132133
status_stage_all: KeyEvent { code: KeyCode::Char('a'), modifiers: KeyModifiers::empty()},
134+
status_stage_update: KeyEvent { code: KeyCode::Char('u'), modifiers: KeyModifiers::empty()},
133135
status_reset_item: KeyEvent { code: KeyCode::Char('D'), modifiers: KeyModifiers::SHIFT},
134136
diff_reset_lines: KeyEvent { code: KeyCode::Char('d'), modifiers: KeyModifiers::empty()},
135137
status_ignore_file: KeyEvent { code: KeyCode::Char('i'), modifiers: KeyModifiers::empty()},

src/strings.rs

+10
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,16 @@ pub mod commands {
767767
CMD_GROUP_CHANGES,
768768
)
769769
}
770+
pub fn stage_update(key_config: &SharedKeyConfig) -> CommandText {
771+
CommandText::new(
772+
format!(
773+
"Stage Only Tracked Files [{}]",
774+
key_config.get_hint(key_config.status_stage_update),
775+
),
776+
"stage all changes (in already tracked, unstaged files)",
777+
CMD_GROUP_CHANGES,
778+
)
779+
}
770780
pub fn unstage_item(key_config: &SharedKeyConfig) -> CommandText {
771781
CommandText::new(
772782
format!(

vim_style_key_config.ron

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// bit for modifiers
2-
// bits: 0 None
2+
// bits: 0 None
33
// bits: 1 SHIFT
44
// bits: 2 CONTROL
55
//
@@ -33,7 +33,7 @@
3333

3434
open_commit: ( code: Char('c'), modifiers: ( bits: 0,),),
3535
// Note: the shift modifier does not work for open_commit_editor
36-
// Also just plain text characters will not work because the commit
36+
// Also just plain text characters will not work because the commit
3737
// msg editor will interpret them as text input
3838
open_commit_editor: ( code: Char('e'), modifiers: ( bits: 2,),),
3939
undo_commit: ( code: Char('U'), modifiers: ( bits: 1,),),
@@ -58,6 +58,7 @@
5858
edit_file: ( code: Char('I'), modifiers: ( bits: 1,),),
5959

6060
status_stage_all: ( code: Char('a'), modifiers: ( bits: 0,),),
61+
status_stage_update: ( code: Char('u'), modifiers: ( bits: 0,),),
6162
status_reset_item: ( code: Char('U'), modifiers: ( bits: 1,),),
6263
status_ignore_file: ( code: Char('i'), modifiers: ( bits: 0,),),
6364

0 commit comments

Comments
 (0)