-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Stash apply #3018
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stash apply #3018
Conversation
Thanks @ethomson, awesome work 👍 |
BTW is this for performance reasons or API impedance reasons? |
@ethomson Any idea when this will land? |
Thank you @swisspol for all your awesome work - I literally cannot express how painful it was to understand how stash application actually works in Git. Crazy. So thanks for digging into this. And again, sorry to bikeshed all over this (and to take so long to do so) - this was mostly for perf (I work in a very large tree, a write/read of that is expensive.) |
This doesn't seem to return that, testing it under libgit2sharp as we speak. |
Do you mean it returns a different error code or it succeeds? |
I get different errors by following the docs with c31a1ed:
But I got the stash applied successfully? Probably I did something wrong, someone should look at the tests to see if I approached them correctly. |
This is the expected git stash behavior (don't ask, git stash is a mess): polbookpro:test pol$ git init
Initialized empty Git repository in /private/tmp/test/.git/
polbookpro:test pol$ git commit --allow-empty -m "Initial commit"
[master (root-commit) 1ec5ac0] Initial commit
polbookpro:test pol$ date > file
polbookpro:test pol$ git add file
polbookpro:test pol$ git stash
Saved working directory and index state WIP on master: 1ec5ac0 Initial commit
HEAD is now at 1ec5ac0 Initial commit
polbookpro:test pol$ git stash pop
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: file
Dropped refs/stash@{0} (341999004335d04dbc782d2dce198b2ec3e56d03) And again with the
Same end result, I know makes no sense. That's why I wrote stash API in libgit2 to mirror exactly how it's implemented in Git CLT (even if it was so convoluted for no good reason). You have all these peculiar behaviors you need to preserve.
polbookpro:test pol$ git init
Initialized empty Git repository in /private/tmp/test/.git/
polbookpro:test pol$ git commit --allow-empty -m "Initial commit"
[master (root-commit) 5756c56] Initial commit
polbookpro:test pol$ date > file1
polbookpro:test pol$ date > file2
polbookpro:test pol$ git add file1
polbookpro:test pol$ git stash <----- Note that in your test this won't stash "file2" since you don't include
Saved working directory and index state WIP on master: 5756c56 Initial commit
HEAD is now at 5756c56 Initial commit
polbookpro:test pol$ date > file1
polbookpro:test pol$ date > file2 <---- Doesn't affect stash
polbookpro:test pol$ git add file1
polbookpro:test pol$ git stash pop --index
error: file1: already exists in index
Conflicts in index. Try without --index. Now if I try to apply that same stash using libgit2's API and with If I use |
Okay, I'm starting to understand how it works. Thanks for the clarifications. I'll study git.git behavior on stash and update the tests to match that behavior. Thanks a lot. There is a possibility I'm wrong, and not something that @ethomson changed screwing stuff. |
@ethomson I just found this tiny bug, you should pick it up if you haven't already fixed it: |
if ((flags & GIT_APPLY_REINSTATE_INDEX) && | ||
git_oid_cmp(git_tree_id(stash_parent_tree), git_tree_id(index_tree))) { | ||
|
||
if ((error = merge_index_and_tree( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that the checkout options for notifying checkout paths isn't used here and below, right?
It's only reporting untracked files for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/cc @ethomson
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that the checkout options for notifying checkout paths isn't used here and below, right?
It's only reporting untracked files for me.
Can you provide a unit test or something to repro this with?
Yes, I dropped the Otherwise, I'm having a bit of trouble following this thread. Can one of you two summarize the status here? Is something else broken? If so, what? |
I was unaware of git stash's behavior in case of reinstate index or normal. If EEXISTS was dropped, then it's fine by me. Otherwise, there's no checkout notification called for the updated files. |
So... I restored the Do we really need two different exit codes here? Could we use the checkout notify callbacks to discern between this case and other checkout conflicts? I would much prefer to simply return Otherwise, @Therzok - I think the behavior that you're seeing was because if you explicitly provide a checkout option, then you must also provide a checkout strategy that is not NONE (or else we won't do anything)... I added a test here, but: I added a change to imply a checkout strategy of |
Sorry @ethomson I missed some comments here. The point is AFAIK my original implementation is "canonical" since based on exactly the same heuristics as Git shell script. @Therzok noticed some suspect behavior which partially went away when switching back to my original implementation. So looks one of your change introduced a regression. Not sure why it wasn't captured by the existing unit tests though. Looks like with your latest changes in, @Therzok would need to confirm his tests behave same with both my original implementation and yours.
It's like |
My understanding is that @Therzok was testing to the documentation, which I had not updated to reflect my removal of the I have since updated to replace that behavior that I had removed but I think this is the wrong way to signal that condition and we should find something superior. |
Okay, well, there was a resounding silence there. So I've dropped the whole I've added a progress callback to the options structure that you can use if you want to be able to discern one checkout conflict from another. Seem sensible? |
Where are we standing unit test wise now? Are the unit tests I originally written all pass unmodified with your latest version? That's the most important thing to me. Then I'm curious to hear about @Therzok's functional tests and if the results are now the same for both my original implementation and yours. I'm afraid I don't have feedback on the |
I'm completely fine with the modified implementation BTW, I'd just like confirmation that the tests are passing all the way up the stack. |
The only change that I made to your tests was to assert the return code to |
Sounds like that if @Therzok's tests are now passing, this should most likely land finally :) |
Yes, it's all working properly now. I'll update all of libgit2sharp's tests tomorrow. I'm only curious as of why aren't the Checkout options themselves used for providing the notification callbacks. But this works for me. |
Cool. I updated this with I also included @swisspol 's patch (above). |
I was under the impression the unit tests I added to libgit2 were covering all cases, but I must have missed something as there's a regression with this PR compared to my original implementation (some of my higher-level functional tests now fail): Start with this state: On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: bar.txt
modified: hello_world.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: foo.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt Then do Using
However using libgit2's
Notice that |
And here's another regression (possibly the same underlying issue): Start with this state:
Then do git Using
However using libgit2's
Notice how the index wasn't restored at all. |
I feel like there should be unit tests for this! (In the libgit2 tests, I mean.) |
I agree. What do you make of the 2nd case though? It seems |
Hey @ethomson so what do you wanna do about the above issues? Current stash apply in libgit2 is not working like Git core unfortunately. |
I think that the first step would be to add failing unit tests. I'm heads-down right now, so if that's something you can do, that would be great. |
Sorry for the noise above... It's because I have to continuously revert this stash apply implementation in my fork of libgit2, since it doesn't behave like Git CLT. |
I created a dedicated issue to track this regression: #3230. |
A modification on #2705. I made some notes in that PR, though actually acting on those notes was quite a bit more challenging than I had hoped. This introduces two lower level ideas:
git_merge__iterators
, to allow one to merge a tree and an index and get an index out as a result. (Interestinglygit_merge
had this broken out as a helper function at one point, and it was brought back in. So now it's a helper function again.) This allows us to merge the results of another merge, without having to write it out to a tree.git_index_read_index
, which behaves likegit_index_read_tree
except that it reads an index into the index. This maintains the stat cache of the existing items.Sorry about the long delay in finishing this up. Stash application is completely and totally ridiculous (put these files on disk and these in the index unless this happens or this), as I'm sure @swisspol can attest to. I think that with the two basic functions mentioned above that the
stash_apply
can be a bit simpler and not necessarily have to write objects for the intermediate steps.