1
0
Fork 0
mirror of https://github.com/helix-editor/helix synced 2024-06-03 08:26:10 +02:00
helix/helix-vcs/src/git.rs
Pascal Kuthe 5a3ff74221
Show (git) diff signs in gutter (#3890)
* Show (git) diff signs in gutter (#3890)

Avoid string allocation when git diffing

Incrementally diff using changesets

refactor diffs to be provider indepndent and improve git implementation

remove dependency on zlib-ng

switch to asynchronus diffing with similar

Update helix-vcs/Cargo.toml

fix toml formatting

Co-authored-by: Ivan Tham <pickfire@riseup.net>

fix typo in documentation

use ropey reexpors from helix-core

fix crash when creating new file

remove useless use if io::Cursor

fix spelling mistakes

implement suggested improvement to repository loading

improve git test isolation

remove lefover comments

Co-authored-by: univerz <univerz@fu-solution.com>

fixed spelling mistake

minor cosmetic changes

fix: set self.differ to None if decoding the diff_base fails

fixup formatting

Co-authored-by: Ivan Tham <pickfire@riseup.net>

reload diff_base when file is reloaded from disk

switch to imara-diff

Fixup formatting

Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>

Redraw buffer whenever a diff is updated.

Only store hunks instead of changes for individual lines to easily allow
jumping between them

Update to latest gitoxide version

Change default diff gutter position

Only update gutter after timeout

* update diff gutter synchronously, with a timeout

* Apply suggestions from code review

Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>

* address review comments and ensure lock is always aquired

* remove configuration for redraw timeout

Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
2022-12-01 17:35:23 +09:00

81 lines
2.7 KiB
Rust

use std::path::Path;
use git::objs::tree::EntryMode;
use git::sec::trust::DefaultForLevel;
use git::{Commit, ObjectId, Repository, ThreadSafeRepository};
use git_repository as git;
use crate::DiffProvider;
#[cfg(test)]
mod test;
pub struct Git;
impl Git {
fn open_repo(path: &Path, ceiling_dir: Option<&Path>) -> Option<ThreadSafeRepository> {
// custom open options
let mut git_open_opts_map = git::sec::trust::Mapping::<git::open::Options>::default();
// don't use the global git configs (not needed)
let config = git::permissions::Config {
system: false,
git: false,
user: false,
env: true,
includes: true,
git_binary: false,
};
// change options for config permissions without touching anything else
git_open_opts_map.reduced = git_open_opts_map.reduced.permissions(git::Permissions {
config,
..git::Permissions::default_for_level(git::sec::Trust::Reduced)
});
git_open_opts_map.full = git_open_opts_map.full.permissions(git::Permissions {
config,
..git::Permissions::default_for_level(git::sec::Trust::Full)
});
let mut open_options = git::discover::upwards::Options::default();
if let Some(ceiling_dir) = ceiling_dir {
open_options.ceiling_dirs = vec![ceiling_dir.to_owned()];
}
ThreadSafeRepository::discover_with_environment_overrides_opts(
path,
open_options,
git_open_opts_map,
)
.ok()
}
}
impl DiffProvider for Git {
fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>> {
debug_assert!(!file.exists() || file.is_file());
debug_assert!(file.is_absolute());
// TODO cache repository lookup
let repo = Git::open_repo(file.parent()?, None)?.to_thread_local();
let head = repo.head_commit().ok()?;
let file_oid = find_file_in_commit(&repo, &head, file)?;
let file_object = repo.find_object(file_oid).ok()?;
Some(file_object.detach().data)
}
}
/// Finds the object that contains the contents of a file at a specific commit.
fn find_file_in_commit(repo: &Repository, commit: &Commit, file: &Path) -> Option<ObjectId> {
let repo_dir = repo.work_dir()?;
let rel_path = file.strip_prefix(repo_dir).ok()?;
let tree = commit.tree().ok()?;
let tree_entry = tree.lookup_entry_by_path(rel_path).ok()??;
match tree_entry.mode() {
// not a file, everything is new, do not show diff
EntryMode::Tree | EntryMode::Commit | EntryMode::Link => None,
// found a file
EntryMode::Blob | EntryMode::BlobExecutable => Some(tree_entry.object_id()),
}
}