Reputation: 3770
I am trying to build a hobby app to provide a UI for Git. I am trying to perform the equivalent of git restore --staged <file>
. The below seems to successfully perform the correct action in the case where the file is newly added to the index, so I can just remove it form the index, but I am not sure how to handle the case where the file has previously been added to the index and has been modified. I have a limited understanding of both Git and C which makes it hard for me to work this out myself.
use git2;
fn main() {
let repo = git2::Repository::open("myrepo").unwrap();
let mut index = repo.index().unwrap();
let statuses = repo.statuses(None).unwrap();
let file_status_entry = statuses.get(0).unwrap();
let ct_path = std::path::Path::new(file_status_entry.path().unwrap());
let file_status = file_status_entry.status();
if file_status.is_index_new() {
index.remove(ct_path, 0).unwrap();
} else {
// not sure what to do here
index.remove(ct_path, 0).unwrap();
}
index.write().unwrap();
}
Upvotes: 0
Views: 190
Reputation: 488253
The git restore
command is ... complicated. Emulating it exactly is therefore also complicated, regardless of what language and library you use. However, if you limit yourself to just one instance of git restore
's behavior (e.g., git restore --staged
), that simplifies things.
I'm not familiar with the Rust library version here, so all I can tell you is what CGit does with git restore --staged path
. The goal of this operation is to copy some path into Git's index, from some source commit. When --staged
is used, the source commit defaults to HEAD
(unless overridden with --source
). The working tree is left untouched by this operation (unless you add --worktree
as well of course).
The Rust equivalent will amount to:
At this point you're ready for the logic. Note: for git restore
, the argument is not a path name but rather a pathspec, which requires iterating over all matching path names in various cases (e.g., git restore --staged "*"
). This can affect the logic below (if you're git restore
-ing */foo
and there are six directories that could have a foo
but only four of them do have a foo
, the "no match" case should not error out for the other two directories). If you're handling only a single path name your job is simplified and is what is listed below.
error: pathspec 'foo' did not match any file(s) know to git
).Once you've done this for a single file (no pathspec magic, just one file listed) or all appropriate files (globbing or whatever), if you've updated the index, it is now time to put it back. This involves writing the new content index to the index.lock
file obtained as the lock in the second initial step, flushing that to disk (be sure to fsync
it if the OS has fsync
), and renaming it to release the lock and put it in place as the index.
Remember, too, to handle any git worktree add
complications (these result in a different default index file) and/or GIT_INDEX_FILE
environment complications. These may (or may not) be covered by the library.
Upvotes: 1