git.rs

  1pub mod blame;
  2pub mod commit;
  3mod hosting_provider;
  4mod remote;
  5pub mod repository;
  6pub mod status;
  7
  8pub use crate::hosting_provider::*;
  9pub use crate::remote::*;
 10use anyhow::{Context as _, Result};
 11pub use git2 as libgit;
 12use gpui::action_with_deprecated_aliases;
 13use gpui::actions;
 14use gpui::impl_action_with_deprecated_aliases;
 15pub use repository::WORK_DIRECTORY_REPO_PATH;
 16use schemars::JsonSchema;
 17use serde::{Deserialize, Serialize};
 18use std::ffi::OsStr;
 19use std::fmt;
 20use std::str::FromStr;
 21use std::sync::LazyLock;
 22
 23pub static DOT_GIT: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new(".git"));
 24pub static GITIGNORE: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new(".gitignore"));
 25pub static FSMONITOR_DAEMON: LazyLock<&'static OsStr> =
 26    LazyLock::new(|| OsStr::new("fsmonitor--daemon"));
 27pub static LFS_DIR: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new("lfs"));
 28pub static COMMIT_MESSAGE: LazyLock<&'static OsStr> =
 29    LazyLock::new(|| OsStr::new("COMMIT_EDITMSG"));
 30pub static INDEX_LOCK: LazyLock<&'static OsStr> = LazyLock::new(|| OsStr::new("index.lock"));
 31
 32actions!(
 33    git,
 34    [
 35        // per-hunk
 36        ToggleStaged,
 37        StageAndNext,
 38        UnstageAndNext,
 39        // per-file
 40        StageFile,
 41        UnstageFile,
 42        // repo-wide
 43        StageAll,
 44        UnstageAll,
 45        RestoreTrackedFiles,
 46        TrashUntrackedFiles,
 47        Uncommit,
 48        Push,
 49        ForcePush,
 50        Pull,
 51        Fetch,
 52        Commit,
 53        Amend,
 54        Cancel,
 55        ExpandCommitEditor,
 56        GenerateCommitMessage,
 57        Init,
 58    ]
 59);
 60
 61#[derive(Clone, Debug, Default, PartialEq, Deserialize, JsonSchema)]
 62pub struct RestoreFile {
 63    #[serde(default)]
 64    pub skip_prompt: bool,
 65}
 66
 67impl_action_with_deprecated_aliases!(git, RestoreFile, ["editor::RevertFile"]);
 68action_with_deprecated_aliases!(git, Restore, ["editor::RevertSelectedHunks"]);
 69action_with_deprecated_aliases!(git, Blame, ["editor::ToggleGitBlame"]);
 70
 71/// The length of a Git short SHA.
 72pub const SHORT_SHA_LENGTH: usize = 7;
 73
 74#[derive(Clone, Copy, Eq, Hash, PartialEq)]
 75pub struct Oid(libgit::Oid);
 76
 77impl Oid {
 78    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
 79        let oid = libgit::Oid::from_bytes(bytes).context("failed to parse bytes into git oid")?;
 80        Ok(Self(oid))
 81    }
 82
 83    pub fn as_bytes(&self) -> &[u8] {
 84        self.0.as_bytes()
 85    }
 86
 87    pub(crate) fn is_zero(&self) -> bool {
 88        self.0.is_zero()
 89    }
 90
 91    /// Returns this [`Oid`] as a short SHA.
 92    pub fn display_short(&self) -> String {
 93        self.to_string().chars().take(SHORT_SHA_LENGTH).collect()
 94    }
 95}
 96
 97impl FromStr for Oid {
 98    type Err = anyhow::Error;
 99
100    fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
101        libgit::Oid::from_str(s)
102            .context("parsing git oid")
103            .map(Self)
104    }
105}
106
107impl fmt::Debug for Oid {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        fmt::Display::fmt(self, f)
110    }
111}
112
113impl fmt::Display for Oid {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        self.0.fmt(f)
116    }
117}
118
119impl Serialize for Oid {
120    fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
121    where
122        S: serde::Serializer,
123    {
124        serializer.serialize_str(&self.0.to_string())
125    }
126}
127
128impl<'de> Deserialize<'de> for Oid {
129    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
130    where
131        D: serde::Deserializer<'de>,
132    {
133        let s = String::deserialize(deserializer)?;
134        s.parse::<Oid>().map_err(serde::de::Error::custom)
135    }
136}
137
138impl Default for Oid {
139    fn default() -> Self {
140        Self(libgit::Oid::zero())
141    }
142}
143
144impl From<Oid> for u32 {
145    fn from(oid: Oid) -> Self {
146        let bytes = oid.0.as_bytes();
147        debug_assert!(bytes.len() > 4);
148
149        let mut u32_bytes: [u8; 4] = [0; 4];
150        u32_bytes.copy_from_slice(&bytes[..4]);
151
152        u32::from_ne_bytes(u32_bytes)
153    }
154}
155
156impl From<Oid> for usize {
157    fn from(oid: Oid) -> Self {
158        let bytes = oid.0.as_bytes();
159        debug_assert!(bytes.len() > 8);
160
161        let mut u64_bytes: [u8; 8] = [0; 8];
162        u64_bytes.copy_from_slice(&bytes[..8]);
163
164        u64::from_ne_bytes(u64_bytes) as usize
165    }
166}