| @@ -5,7 +5,7 @@ use gix::index::Entry; · 4 unmodified lines |
| 5 | use gix::prelude::FindExt; | 5 | use gix::prelude::FindExt; |
| 6 | use gix::Progress; | 6 | use gix::Progress; |
| 7 | use gix_status::index_as_worktree::traits::FastEq; | 7 | use gix_status::index_as_worktree::traits::FastEq; |
| 8 | use gix_status::index_as_worktree::Change; | 8 | use gix_status::index_as_worktree::{Change, Conflict, EntryStatus}; |
| 9 | | 9 | |
| 10 | pub enum Submodules { | 10 | pub enum Submodules { |
| 11 | /// display all information about submodules, including ref changes, modifications and untracked files. | 11 | /// display all information about submodules, including ref changes, modifications and untracked files. |
| @@ -48,23 +48,42 @@ pub fn show( · 36 unmodified lines |
| 48 | )?; | 48 | )?; |
| 49 | let mut progress = progress.add_child("traverse index"); | 49 | let mut progress = progress.add_child("traverse index"); |
| 50 | let start = std::time::Instant::now(); | 50 | let start = std::time::Instant::now(); |
| | 51 | let options = gix_status::index_as_worktree::Options { |
| | 52 | fs: repo.filesystem_options()?, |
| | 53 | thread_limit, |
| | 54 | stat: repo.stat_options()?, |
| | 55 | attributes: match repo |
| | 56 | .attributes_only( |
| | 57 | index, |
| | 58 | gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping, |
| | 59 | )? |
| | 60 | .detach() |
| | 61 | .state_mut() |
| | 62 | { |
| | 63 | gix::worktree::stack::State::AttributesStack(attrs) => std::mem::take(attrs), |
| | 64 | // TODO: this should be nicer by creating attributes directly, but it's a private API |
| | 65 | _ => unreachable!("state must be attributes stack only"), |
| | 66 | }, |
| | 67 | }; |
| 51 | gix_status::index_as_worktree( | 68 | gix_status::index_as_worktree( |
| 52 | index, | 69 | index, |
| 53 | repo.work_dir() | 70 | repo.work_dir() |
| 54 | .context("This operation cannot be run on a bare repository")?, | 71 | .context("This operation cannot be run on a bare repository")?, |
| 55 | &mut Printer(out), | 72 | &mut Printer(out), |
| 56 | FastEq, | 73 | FastEq, |
| | 74 | Submodule, |
| 57 | { | 75 | { |
| 58 | let odb = repo.objects.clone().into_arc()?; | 76 | let odb = repo.objects.clone().into_arc()?; |
| 59 | move |id, buf| odb.find_blob(id, buf) | 77 | move |id, buf| odb.find_blob(id, buf) |
| 60 | }, | 78 | }, |
| 61 | &mut progress, | 79 | &mut progress, |
| 62 | pathspec.detach()?, | 80 | pathspec.detach()?, |
| 63 | gix_status::index_as_worktree::Options { | 81 | repo.filter_pipeline(Some(gix::hash::ObjectId::empty_tree(repo.object_hash())))? |
| 64 | fs: repo.filesystem_options()?, | 82 | .0 |
| 65 | thread_limit, | 83 | .into_parts() |
| 66 | stat: repo.stat_options()?, | 84 | .0, |
| 67 | }, | 85 | &gix::interrupt::IS_INTERRUPTED, |
| | 86 | options, |
| 68 | )?; | 87 | )?; |
| 69 | | 88 | |
| 70 | writeln!(err, "\nhead -> index and untracked files aren't implemented yet")?; | 89 | writeln!(err, "\nhead -> index and untracked files aren't implemented yet")?; |
| @@ -72,6 +91,18 @@ pub fn show( · 1 unmodified line |
| 72 | Ok(()) | 91 | Ok(()) |
| 73 | } | 92 | } |
| 74 | | 93 | |
| | 94 | #[derive(Clone)] |
| | 95 | struct Submodule; |
| | 96 | |
| | 97 | impl gix_status::index_as_worktree::traits::SubmoduleStatus for Submodule { |
| | 98 | type Output = (); |
| | 99 | type Error = std::convert::Infallible; |
| | 100 | |
| | 101 | fn status(&mut self, _entry: &Entry, _rela_path: &BStr) -> Result<Option<Self::Output>, Self::Error> { |
| | 102 | Ok(None) |
| | 103 | } |
| | 104 | } |
| | 105 | |
| 75 | struct Printer<W>(W); | 106 | struct Printer<W>(W); |
| 76 | | 107 | |
| 77 | impl<'index, W> gix_status::index_as_worktree::VisitEntry<'index> for Printer<W> | 108 | impl<'index, W> gix_status::index_as_worktree::VisitEntry<'index> for Printer<W> |
| @@ -79,42 +110,54 @@ where · 1 unmodified line |
| 79 | W: std::io::Write, | 110 | W: std::io::Write, |
| 80 | { | 111 | { |
| 81 | type ContentChange = (); | 112 | type ContentChange = (); |
| | 113 | type SubmoduleStatus = (); |
| 82 | | 114 | |
| 83 | fn visit_entry( | 115 | fn visit_entry( |
| 84 | &mut self, | 116 | &mut self, |
| 85 | entry: &'index Entry, | 117 | _entries: &'index [Entry], |
| | 118 | _entry: &'index Entry, |
| | 119 | _entry_index: usize, |
| 86 | rela_path: &'index BStr, | 120 | rela_path: &'index BStr, |
| 87 | change: Option<Change<Self::ContentChange>>, | 121 | status: EntryStatus<Self::ContentChange>, |
| 88 | conflict: bool, | | |
| 89 | ) { | 122 | ) { |
| 90 | self.visit_inner(entry, rela_path, change, conflict).ok(); | 123 | self.visit_inner(rela_path, status).ok(); |
| 91 | } | 124 | } |
| 92 | } | 125 | } |
| 93 | | 126 | |
| 94 | impl<W: std::io::Write> Printer<W> { | 127 | impl<W: std::io::Write> Printer<W> { |
| 95 | fn visit_inner( | 128 | fn visit_inner(&mut self, rela_path: &BStr, status: EntryStatus<()>) -> std::io::Result<()> { |
| 96 | &mut self, | 129 | let char_storage; |
| 97 | _entry: &Entry, | 130 | let status = match status { |
| 98 | rela_path: &BStr, | 131 | EntryStatus::Conflict(conflict) => as_str(conflict), |
| 99 | change: Option<Change<()>>, | 132 | EntryStatus::Change(change) => { |
| 100 | conflict: bool, | 133 | char_storage = change_to_char(&change); |
| 101 | ) -> anyhow::Result<()> { | 134 | std::str::from_utf8(std::slice::from_ref(&char_storage)).expect("valid ASCII") |
| 102 | if let Some(change) = conflict | 135 | } |
| 103 | .then_some('U') | 136 | EntryStatus::NeedsUpdate(_) => return Ok(()), |
| 104 | .or_else(|| change.as_ref().and_then(change_to_char)) | 137 | EntryStatus::IntentToAdd => "A", |
| 105 | { | 138 | }; |
| 106 | writeln!(&mut self.0, "{change} {rela_path}")?; | 139 | |
| 107 | } | 140 | writeln!(&mut self.0, "{status: >3} {rela_path}") |
| 108 | Ok(()) | | |
| 109 | } | 141 | } |
| 110 | } | 142 | } |
| 111 | | 143 | |
| 112 | fn change_to_char(change: &Change<()>) -> Option<char> { | 144 | fn as_str(c: Conflict) -> &'static str { |
| | 145 | match c { |
| | 146 | Conflict::BothDeleted => "DD", |
| | 147 | Conflict::AddedByUs => "AU", |
| | 148 | Conflict::DeletedByThem => "UD", |
| | 149 | Conflict::AddedByThem => "UA", |
| | 150 | Conflict::DeletedByUs => "DU", |
| | 151 | Conflict::BothAdded => "AA", |
| | 152 | Conflict::BothModified => "UU", |
| | 153 | } |
| | 154 | } |
| | 155 | |
| | 156 | fn change_to_char(change: &Change<()>) -> u8 { |
| 113 | // Known status letters: https://github.com/git/git/blob/6807fcfedab84bc8cd0fbf721bc13c4e68cda9ae/diff.h#L613 | 157 | // Known status letters: https://github.com/git/git/blob/6807fcfedab84bc8cd0fbf721bc13c4e68cda9ae/diff.h#L613 |
| 114 | Some(match change { | 158 | match change { |
| 115 | Change::Removed => 'D', | 159 | Change::Removed => b'D', |
| 116 | Change::Type => 'T', | 160 | Change::Type => b'T', |
| 117 | Change::Modification { .. } => 'M', | 161 | Change::SubmoduleModification(_) | Change::Modification { .. } => b'M', |
| 118 | Change::IntentToAdd => return None, | 162 | } |
| 119 | }) | | |
| 120 | } | 163 | } |