How to “unstage” a file with Objective-Git (libgit

2019-09-12 11:32发布

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.

2条回答
Melony?
2楼-- · 2019-09-12 11:48

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:

- Fetch head's GTReference using GTRepository.headReferenceWithError:
- If the reference resolves to a GTCommit =>
    - Fetch the GTIndexEntry using GTIndex.entryWithPath:
    - If we have an index entry =>
        - Fetch the GTTree using GTCommit.tree
        - Fetch the GTTreeEntry using GTTree.entryWithPath:error:
        - If GTTreeEntry.SHA == GTIndexEntry.OID.SHA =>
            - GTIndex.addFile:error:
            (using this approach, it's possible to end up with identical
             tree and index entries, which means the file is unstaged)
        - Else if GTTreeEntry.type == GTObjectTypeBlob =>
            - Fetch GTBlob using GTTreeEntry.GTObject:error:
            - Pass GTBlob.data to GTIndex.addData:withPath:error:
        - Else if no GTTreeEntry => GTIndex.removeFile:error:
    - Else add the file/directory to the GTIndex (since we have no index entry)
    - Write the changes to disk with GTIndex.write:

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:

  • GTIndexEntry.status is useless in this scenario; it references some flags that are used internally by libgit2 during the construction of the index but are not helpful for determining the status of files afterward (I'm frankly not sure why these flags are exposed by Objective-Git at all)
  • If you want to un/stage all files in the working directory, you can have Objective-Git do a lot of the legwork using GTDiff's +diffIndexFromTree:inRepository:options:error:
查看更多
祖国的老花朵
3楼-- · 2019-09-12 12:08

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, and id. You can copy those directly from the tree entry. Then use the the objective-git equivalent of git_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.

查看更多
登录 后发表回答