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        FetchFrom,
 53        Commit,
 54        Amend,
 55        Cancel,
 56        ExpandCommitEditor,
 57        GenerateCommitMessage,
 58        Init,
 59    ]
 60);
 61
 62#[derive(Clone, Debug, Default, PartialEq, Deserialize, JsonSchema)]
 63pub struct RestoreFile {
 64    #[serde(default)]
 65    pub skip_prompt: bool,
 66}
 67
 68impl_action_with_deprecated_aliases!(git, RestoreFile, ["editor::RevertFile"]);
 69action_with_deprecated_aliases!(git, Restore, ["editor::RevertSelectedHunks"]);
 70action_with_deprecated_aliases!(git, Blame, ["editor::ToggleGitBlame"]);
 71
 72/// The length of a Git short SHA.
 73pub const SHORT_SHA_LENGTH: usize = 7;
 74
 75#[derive(Clone, Copy, Eq, Hash, PartialEq)]
 76pub struct Oid(libgit::Oid);
 77
 78impl Oid {
 79    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
 80        let oid = libgit::Oid::from_bytes(bytes).context("failed to parse bytes into git oid")?;
 81        Ok(Self(oid))
 82    }
 83
 84    pub fn as_bytes(&self) -> &[u8] {
 85        self.0.as_bytes()
 86    }
 87
 88    pub(crate) fn is_zero(&self) -> bool {
 89        self.0.is_zero()
 90    }
 91
 92    /// Returns this [`Oid`] as a short SHA.
 93    pub fn display_short(&self) -> String {
 94        self.to_string().chars().take(SHORT_SHA_LENGTH).collect()
 95    }
 96}
 97
 98impl FromStr for Oid {
 99    type Err = anyhow::Error;
100
101    fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
102        libgit::Oid::from_str(s)
103            .context("parsing git oid")
104            .map(Self)
105    }
106}
107
108impl fmt::Debug for Oid {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        fmt::Display::fmt(self, f)
111    }
112}
113
114impl fmt::Display for Oid {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        self.0.fmt(f)
117    }
118}
119
120impl Serialize for Oid {
121    fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
122    where
123        S: serde::Serializer,
124    {
125        serializer.serialize_str(&self.0.to_string())
126    }
127}
128
129impl<'de> Deserialize<'de> for Oid {
130    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
131    where
132        D: serde::Deserializer<'de>,
133    {
134        let s = String::deserialize(deserializer)?;
135        s.parse::<Oid>().map_err(serde::de::Error::custom)
136    }
137}
138
139impl Default for Oid {
140    fn default() -> Self {
141        Self(libgit::Oid::zero())
142    }
143}
144
145impl From<Oid> for u32 {
146    fn from(oid: Oid) -> Self {
147        let bytes = oid.0.as_bytes();
148        debug_assert!(bytes.len() > 4);
149
150        let mut u32_bytes: [u8; 4] = [0; 4];
151        u32_bytes.copy_from_slice(&bytes[..4]);
152
153        u32::from_ne_bytes(u32_bytes)
154    }
155}
156
157impl From<Oid> for usize {
158    fn from(oid: Oid) -> Self {
159        let bytes = oid.0.as_bytes();
160        debug_assert!(bytes.len() > 8);
161
162        let mut u64_bytes: [u8; 8] = [0; 8];
163        u64_bytes.copy_from_slice(&bytes[..8]);
164
165        u64::from_ne_bytes(u64_bytes) as usize
166    }
167}