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