1use std::path::{Path, PathBuf};
2
3use eyre::Context;
4use tracing::instrument;
5
6use crate::core::eventlog::EventTransactionId;
7
8use super::{FileMode, GitRunInfo, GitRunOpts, GitRunResult, MaybeZeroOid, NonZeroOid, Repo};
9
10#[derive(Copy, Clone, Debug)]
12pub enum Stage {
13 Stage0,
15
16 Stage1,
18
19 Stage2,
21
22 Stage3,
24}
25
26impl Stage {
27 pub(super) fn get_trailer(&self) -> &'static str {
28 match self {
29 Stage::Stage0 => "Branchless-stage-0",
30 Stage::Stage1 => "Branchless-stage-1",
31 Stage::Stage2 => "Branchless-stage-2",
32 Stage::Stage3 => "Branchless-stage-3",
33 }
34 }
35}
36
37impl From<Stage> for i32 {
38 fn from(stage: Stage) -> Self {
39 match stage {
40 Stage::Stage0 => 0,
41 Stage::Stage1 => 1,
42 Stage::Stage2 => 2,
43 Stage::Stage3 => 3,
44 }
45 }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq, Hash)]
50pub struct IndexEntry {
51 pub(super) oid: MaybeZeroOid,
52 pub(super) file_mode: FileMode,
53}
54
55pub struct Index {
57 pub(super) inner: git2::Index,
58}
59
60impl std::fmt::Debug for Index {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 write!(f, "<Index>")
63 }
64}
65
66impl Index {
67 pub fn has_conflicts(&self) -> bool {
69 self.inner.has_conflicts()
70 }
71
72 pub fn get_entry(&self, path: &Path) -> Option<IndexEntry> {
74 self.get_entry_in_stage(path, Stage::Stage0)
75 }
76
77 pub fn get_entry_in_stage(&self, path: &Path, stage: Stage) -> Option<IndexEntry> {
79 self.inner
80 .get_path(path, i32::from(stage))
81 .map(|entry| IndexEntry {
82 oid: entry.id.into(),
83 file_mode: {
84 let mode = i32::try_from(entry.mode).unwrap();
87 FileMode::from(mode)
88 },
89 })
90 }
91}
92
93#[allow(missing_docs)]
95#[derive(Clone, Debug)]
96pub enum UpdateIndexCommand {
97 Delete {
98 path: PathBuf,
99 },
100 Update {
101 path: PathBuf,
102 stage: Stage,
103 mode: FileMode,
104 oid: NonZeroOid,
105 },
106}
107
108#[instrument]
113pub fn update_index(
114 git_run_info: &GitRunInfo,
115 repo: &Repo,
116 index: &Index,
117 event_tx_id: EventTransactionId,
118 commands: &[UpdateIndexCommand],
119) -> eyre::Result<()> {
120 let stdin = {
121 let mut buf = Vec::new();
122 for command in commands {
123 use std::io::Write;
124
125 match command {
126 UpdateIndexCommand::Delete { path } => {
127 write!(
128 &mut buf,
129 "0 {zero} 0\t{path}\0",
130 zero = MaybeZeroOid::Zero,
131 path = path.display(),
132 )?;
133 }
134
135 UpdateIndexCommand::Update {
136 path,
137 stage,
138 mode,
139 oid,
140 } => {
141 write!(
142 &mut buf,
143 "{mode} {sha1} {stage}\t{path}\0",
144 sha1 = oid,
145 stage = i32::from(*stage),
146 path = path.display(),
147 )?;
148 }
149 }
150 }
151 buf
152 };
153
154 let GitRunResult { .. } = git_run_info
155 .run_silent(
156 repo,
157 Some(event_tx_id),
158 &["update-index", "-z", "--index-info"],
159 GitRunOpts {
160 treat_git_failure_as_error: true,
161 stdin: Some(stdin),
162 },
163 )
164 .wrap_err("Updating index")?;
165 Ok(())
166}