Before this commit, running Git's GC in a Git repo backing a Jujube
repo would risk deleting the conflict data we store as blobs in the
Git repo. This commit fixes that by adding a Git note pointing to the
conflict blob.
I wasn't able to add a test case for this because libgit2 doesn't
support gc [1]. Just testing that the ref is there doesn't seem very
useful.
[1] https://github.com/libgit2/libgit2/issues/3247
Very little code was saved by reusing `write_file()` and it made it
confusing (needed to provide an unused filename). Also, I'll soon want
access to the `locked_repo` variable in `write_conflict()`.
I have never run into this being a problem in practice, but this
change is a stepping stone for two things:
1. Using exponential backoff for other locks (in particular the
working copy).
2. Making the Git store write a ref for conflict objects, so they
don't get GC'd (I want to do that before even I start dogfooding).
I had tried to generate the protobuf code at build time many months
ago, but decided against it because it slowed down the build too
much. I didn't realize there was the
"cargo:rerun-if-changed=<filename>" feature that time. Given that that
exists, it seems like an obvious win to generate the source code at
build time.
I put the generated sources in `$OUT_DIR` (where [1] says they should
be), then include them in the `protos` module by using the `include!`
macro. The biggest problem with that is that I couldn't get IntelliJ
to understand it, even after enabling the experimental features
described in [2].
[1] https://doc.rust-lang.org/cargo/reference/build-script-examples.html#code-generation
[2] https://github.com/intellij-rust/intellij-rust/issues/1908#issuecomment-592773865
We currently recalculate the entire evolution state whenever a new
commit is added within a transaction. That's clearly wasteful. This
commit makes the state-update incremental.
I'm about to make the evolution state updated incrementally. To be
able to tell if a new commit is divergent, we'll need to keep track of
already existing, non-obsolete commits in the change, even if they're
not divergent before the new commit is added.
A pruned commit just indicates that its predecessors should be evolved
onto the pruned commit's parent instead of onto the pruned commit
itself. The pruned commit itself can be divergent. For example, if
there are several pruned sucessors of a commit, then it's unclear
where the predecessor's children should be rebased to.
I'm about to add more fields to the type and this will help to
slightly reduce the boilerplate for initializing and the maps and sets
and creating the struct.
Before this commit, when the `evolve()` evolved a stack of orphans, it
would use the evolve state from the beginning of the function to
calculate where they should go. That meant that only the bottom-most
orphan(s) would get evolved to their right place. This commit fixes
that by use the Transaction's evolution state.
Before this commit, it could share its state with the
`ReadonlyEvolution`. That makes no sense when the state is modified,
and would crash if we tried to get a mutable reference to the
state. It only "worked" because the state is not yet updated within a
transaction (a known bug). I'm about to fix that bug, so we need to
fix the ownership, which this commit does.
There was some duplicate between `ReadonlyEvolution` and
`MutableEvolution` that could be extracted. It will help to have this
shared code on the `State` object for the next few patches.
The point of having the `modified` and `removed` files in the test was
to show that they don't get untracked, but I forgot to include them in
the `.gitignores`, so there was no reason they would have gotten
untracked anyway.
The project's source of truth is now in Git and I really miss support
for anonymous heads and evolution (compared to when the code was in
Mercurial). I'm therefore more motivated to make the tool useful for
day-to-day work on small repos, so I can use it myself. Until now, I
had been more focused on improving performance when it was used as a
read-only client for medium-to-large repos.
One important feature for my day-to-day work is support for
ignores. This commit adds simple and effective, but somewhat hacky
support for that. libgit2 requires a repo to check if a file should be
ignored (presumably so it can respect `.git/info/excludes`). To work
around that, we create a temporary git repo in `/tmp/` whenever the
working copy is committed. We set that temporary git repo's working
copy to be shared with our own working copy. Due to
https://github.com/libgit2/libgit2sharp/issues/1716 (which seems to
apply to the non-.NET version as well), this workaround unfortunately
leaves a .git file (pointing to the deleted temporary git repo) around
in every Jujube repo. That's always ignored by libgit2, so it's not
much of a problem.
This removes one level of indirection, which is nice because it was
visible to the callers. The `Index` struct is now empty. The next step
is obviously to delete it (and perhaps rename `IndexFile` to `Index`
or `ReadonlyIndex`).