I am writing an app that allows performing basic git operations using Objective-Git, but I am having trouble figuring out how to "unstage" a file. More specifically, I have a file with changes that have previously been added to the current GTIndex and I now want to discard those changes (without losing the file's current state on disk).
Here is a rough outline of the relevant logic my program currently follows when toggling the "staged" state of a file:
- Fetch current GTIndex using GTRepository.indexWithError:
- Fetch GTIndexEntry for the file using GTIndex.entryWithPath:
- If the file has an index and GTIndexEntry.status == GTIndexEntryStatusUpdated:
- Here's where I'm stuck
According to this old unanswered question I need to lookup the file's info in the current HEAD. In that case, this is the logic I arrived at:
- Fetch head's GTReference using GTRepository.headReferenceWithError:
- If the reference resolves to a GTCommit object:
- Fetch the GTTree using GTCommit.tree
- Fetch the file's GTTreeEntry using GTTree.entryWithPath:error:
- Stuck again! Do I convert GTTreeEntry to GTIndexEntry somehow?
Any guidance is appreciated! I'm not scared of jumping directly into libgit2 if I have to (this project has already necessitated it once or twice), but since I'm working in Swift I'd like to avoid it if I can and "unstaging" a file seems like such a standard procedure that I figured I must not be understanding how the Objective-Git classes tie together.
After letting this sit a little longer, I discovered that it's actually a lot easier than I was making it; rather than comparing the HEAD tree to the active index, you can simply use GTRepository.statusForFile:success:error: to determine whether the file is staged, unstaged, etc. and then add, remove, or add a blob for the file based on that.
Original approach
(On the off chance it contains useful info for others down the road.)
This turned out to be simpler than it seemed at first, mainly because transitioning between GTTreeEntry and GTIndexEntry is unnecessary. Instead, I simply needed to grab the GTTreeEntry's GTObject (which is a blob for files), then pass the blob's data into the GTIndex.
I ended up with a code flow that goes roughly like this:
A couple of notes in hopes they will be helpful to future generations of people bashing their heads against Objective-Git's largely undocumented hide:
+diffIndexFromTree:inRepository:options:error:
Once you have the tree entry from the HEAD commit you can extract data from it to fill in a new index entry. You don't need to fill in all of the fields, but at least set
path
,mode
, andid
. You can copy those directly from the tree entry. Then use the the objective-git equivalent ofgit_index_add
to update the index with the new entry.If the file didn't exist in the HEAD tree (because it wasn't tracked before), then you can simply remove it from the index with
git_index_remove_bypath
.