sapling/eden/fs/service/EdenServiceHandler.h

179 lines
5.3 KiB
C
Raw Normal View History

/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include "common/fb303/cpp/FacebookBase2.h"
#include "eden/fs/service/gen-cpp2/StreamingEdenService.h"
#include "eden/fs/utils/PathFuncs.h"
namespace folly {
template <typename T>
class Future;
}
namespace facebook {
namespace eden {
class Hash;
Store Hg dirstate data in Hg instead of Eden. Summary: This is a major change to how we manage the dirstate in Eden's Hg extension. Previously, the dirstate information was stored under `$EDEN_CONFIG_DIR`, which is Eden's private storage. Any time the Mercurial extension wanted to read or write the dirstate, it had to make a Thrift request to Eden to do so on its behalf. The upside is that Eden could answer dirstate-related questions independently of the Python code. This was sufficiently different than how Mercurial's default dirstate worked that our subclass, `eden_dirstate`, had to override quite a bit of behavior. Failing to manage the `.hg/dirstate` file in a way similar to the way Mercurial does has exposed some "unofficial contracts" that Mercurial has. For example, tools like Nuclide rely on changes to the `.hg/dirstate` file as a heuristic to determine when to invalidate its internal caches for Mercurial data. Today, Mercurial has a well-factored `dirstatemap` abstraction that is primarily responsible for the transactions with the dirstate's data. With this split, we can focus on putting most of our customizations in our `eden_dirstate_map` subclass while our `eden_dirstate` class has to override fewer methods. Because the data is managed through the `.hg/dirstate` file, transaction logic in Mercurial that relies on renaming/copying that file will work out-of-the-box. This change also reduces the number of Thrift calls the Mercurial extension has to make for operations like `hg status` or `hg add`. In this revision, we introduce our own binary format for the `.hg/dirstate` file. The logic to read and write this file is in `eden/py/dirstate.py`. After the first 40 bytes, which are used for the parent hashes, the next four bytes are reserved for a version number for the file format so we can manage file format changes going forward. Admittedly one downside of this change is that it is a breaking change. Ideally, users should commit all of their local changes in their existing mounts, shutdown Eden, delete the old mounts, restart Eden, and re-clone. In the end, this change deletes a number of Mercurial-specific code and Thrift APIs from Eden. This is a better separation of concerns that makes Eden more SCM-agnostic. For example, this change removes `Dirstate.cpp` and `DirstatePersistance.cpp`, replacing them with the much simpler and more general `Differ.cpp`. The Mercurial-specific logic from `Dirstate.cpp` that turned a diff into an `hg status` now lives in the Mercurial extension in `EdenThriftClient.getStatus()`, which is much more appropriate. Note that this reverts the changes that were recently introduced in D6116105: we now need to intercept `localrepo.localrepository.dirstate` once again. Reviewed By: simpkins Differential Revision: D6179950 fbshipit-source-id: 5b78904909b669c9cc606e2fe1fd118ef6eaab95
2017-11-07 06:44:24 +03:00
class EdenMount;
class EdenServer;
class TreeInode;
/*
* Handler for the EdenService thrift interface
*/
class EdenServiceHandler : virtual public StreamingEdenServiceSvIf,
public facebook::fb303::FacebookBase2 {
public:
explicit EdenServiceHandler(EdenServer* server);
facebook::fb303::cpp2::fb_status getStatus() override;
void mount(std::unique_ptr<MountInfo> info) override;
void unmount(std::unique_ptr<std::string> mountPoint) override;
void listMounts(std::vector<MountInfo>& results) override;
void checkOutRevision(
std::vector<CheckoutConflict>& results,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> hash,
Change how the UNTRACKED_ADDED conflict and merges are handled. Summary: Previously, we used the Mercurial code `g` when faced with an `UNTRACKED_ADDED` file conflict, but that was allowing merges to silently succeed that should not have. This revision changes our logic to use the code `m` for merge, which unearthed that we were not honoring the user's `update.check` setting properly. Because we use `update.check=noconflict` internally at Facebook, we changed the Eden integration tests to default to verifying Hg running with this setting. To support it properly, we had to port this code from `update.py` in Mercurial to our own `_determine_actions_for_conflicts()` function: ``` if updatecheck == 'noconflict': for f, (m, args, msg) in actionbyfile.iteritems(): if m not in ('g', 'k', 'e', 'r', 'pr'): msg = _("conflicting changes") hint = _("commit or update --clean to discard changes") raise error.Abort(msg, hint=hint) ``` However, this introduced an interesting issue where the `checkOutRevision()` Thrift call from Hg would update the `SNAPSHOT` file on the server, but `.hg/dirstate` would not get updated with the new parents until the update completed on the client. With the new call to `raise error.Abort` on the client, we could get in a state where the `SNAPSHOT` file had the hash of the commit assuming the update succeeded, but `.hg/dirstate` reflected the reality where it failed. To that end, we changed `checkOutRevision()` to take a new parameter, `checkoutMode`, which can take on one of three values: `NORMAL`, `DRY_RUN`, and `FORCE`. Now if the user tries to do an ordinary `hg update` with `update.check=noconflict`, we first do a `DRY_RUN` and examine the potential conflicts. Only if the conflicts should not block the update do we proceed with a call to `checkOutRevision()` in `NORMAL` mode. To make this work, we had to make a number of changes to `CheckoutAction`, `CheckoutContext`, `EdenMount`, and `TreeInode` to keep track of the `checkoutMode` and ensure that no changes are made to the working copy when a `DRY_RUN` is in effect. One minor issue (for which there is a `TODO`) is that a `DRY_RUN` will not report any `DIRECTORY_NOT_EMPTY` conflicts that may exist. As `TreeInode` is implemented today, it is a bit messy to report this type of conflict without modifying the working copy along the way. Finally, any `UNTRACKED_ADDED` conflict should cause an update to abort to match the behavior in stock Mercurial if the user has the following config setting: ``` [commands] update.check = noconflict ``` Though the original name for this setting was: ``` [experimental] updatecheck = noconflict ``` Although I am on Mercurial 4.4.1, the `update.check` setting does not seem to take effect when I run the integration tests, but the `updatecheck` setting does, so for now, I set both in `hg_extension_test_base.py` with a `TODO` to remove `updatecheck` once I can get `update.check` to do its job. Reviewed By: simpkins Differential Revision: D6366007 fbshipit-source-id: bb3ecb1270e77d59d7d9e7baa36ada61971bbc49
2017-11-30 08:38:12 +03:00
CheckoutMode checkoutMode) override;
void resetParentCommits(
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<WorkingDirectoryParents> parents) override;
void getBindMounts(
std::vector<std::string>& out,
std::unique_ptr<std::string> mountPoint) override;
void getSHA1(
std::vector<SHA1Result>& out,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::vector<std::string>> paths) override;
void getCurrentJournalPosition(
JournalPosition& out,
std::unique_ptr<std::string> mountPoint) override;
void getFilesChangedSince(
FileDelta& out,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<JournalPosition> fromPosition) override;
void getFileInformation(
std::vector<FileInformationOrError>& out,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::vector<std::string>> paths) override;
implement glob thrift method Summary: This is to facilitate the watchman integration and draws on the watchman glob implementation; the approach is to split the glob strings into path components and evaluate the components step by step as the tree is walked. Components that do not include any glob special characters can be handled as a direct lookup from the directory contents (O(1) rather than O(num-entries)). The glob method returns a set of filenames that match a list of of glob patterns. Recursive globs are supported. It is worth noting that a glob like "**/*" will return a list of every entry in the filesystem. This is potentially expensive and should be avoided. simpkins is in favor of disallowing this as a forcing function to encourage tool-makers to adopt patterns that don't rely on a complete listing of the filesystem. For now I'd like to get this in without such a restriction; it's also worth noting that running `find .` in the root of the mount point has a similar effect and we can't prevent that from happening, so the effect of the overly broad glob is something that we need to be able to withstand in any case. Unrestricted recursive globs will make it easier to connect certain watchman queries in the interim, until we have a more expressive thrift API for walking and filtering the list of files. Note: I've removed the wildmatch flags that I'd put in the API when I stubbed it out originally. Since this is built on top of our GlobMatcher code and that doesn't have those flags, I thought it would be simplest to just remove them. If we find that we need them, we can figure out how to add them later. Also Note: the evaluation of the glob is parallel-ready but currently limited to 1 at a time by constraining the folly::window call to 1. We could make this larger but would need a more intelligent constraint. For example, a recursive glob could initiate N concurrent futures per level where N is the number of sub-dirs at a given level. Using a custom Executor for these futures may be a better option to set an upper bound on the number of concurrent jobs allowed for a given glob call. Depends on D4361197 Reviewed By: simpkins Differential Revision: D4371934 fbshipit-source-id: 444735600bc16d2c2185f2277ddc5b51f672600a
2017-01-26 23:45:50 +03:00
void glob(
std::vector<std::string>& out,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::vector<std::string>> globs) override;
void async_tm_subscribe(
std::unique_ptr<apache::thrift::StreamingHandlerCallback<
std::unique_ptr<JournalPosition>>> callback,
std::unique_ptr<std::string> mountPoint) override;
Store Hg dirstate data in Hg instead of Eden. Summary: This is a major change to how we manage the dirstate in Eden's Hg extension. Previously, the dirstate information was stored under `$EDEN_CONFIG_DIR`, which is Eden's private storage. Any time the Mercurial extension wanted to read or write the dirstate, it had to make a Thrift request to Eden to do so on its behalf. The upside is that Eden could answer dirstate-related questions independently of the Python code. This was sufficiently different than how Mercurial's default dirstate worked that our subclass, `eden_dirstate`, had to override quite a bit of behavior. Failing to manage the `.hg/dirstate` file in a way similar to the way Mercurial does has exposed some "unofficial contracts" that Mercurial has. For example, tools like Nuclide rely on changes to the `.hg/dirstate` file as a heuristic to determine when to invalidate its internal caches for Mercurial data. Today, Mercurial has a well-factored `dirstatemap` abstraction that is primarily responsible for the transactions with the dirstate's data. With this split, we can focus on putting most of our customizations in our `eden_dirstate_map` subclass while our `eden_dirstate` class has to override fewer methods. Because the data is managed through the `.hg/dirstate` file, transaction logic in Mercurial that relies on renaming/copying that file will work out-of-the-box. This change also reduces the number of Thrift calls the Mercurial extension has to make for operations like `hg status` or `hg add`. In this revision, we introduce our own binary format for the `.hg/dirstate` file. The logic to read and write this file is in `eden/py/dirstate.py`. After the first 40 bytes, which are used for the parent hashes, the next four bytes are reserved for a version number for the file format so we can manage file format changes going forward. Admittedly one downside of this change is that it is a breaking change. Ideally, users should commit all of their local changes in their existing mounts, shutdown Eden, delete the old mounts, restart Eden, and re-clone. In the end, this change deletes a number of Mercurial-specific code and Thrift APIs from Eden. This is a better separation of concerns that makes Eden more SCM-agnostic. For example, this change removes `Dirstate.cpp` and `DirstatePersistance.cpp`, replacing them with the much simpler and more general `Differ.cpp`. The Mercurial-specific logic from `Dirstate.cpp` that turned a diff into an `hg status` now lives in the Mercurial extension in `EdenThriftClient.getStatus()`, which is much more appropriate. Note that this reverts the changes that were recently introduced in D6116105: we now need to intercept `localrepo.localrepository.dirstate` once again. Reviewed By: simpkins Differential Revision: D6179950 fbshipit-source-id: 5b78904909b669c9cc606e2fe1fd118ef6eaab95
2017-11-07 06:44:24 +03:00
void getManifestEntry(
ManifestEntry& out,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> relativePath) override;
Store Hg dirstate data in Hg instead of Eden. Summary: This is a major change to how we manage the dirstate in Eden's Hg extension. Previously, the dirstate information was stored under `$EDEN_CONFIG_DIR`, which is Eden's private storage. Any time the Mercurial extension wanted to read or write the dirstate, it had to make a Thrift request to Eden to do so on its behalf. The upside is that Eden could answer dirstate-related questions independently of the Python code. This was sufficiently different than how Mercurial's default dirstate worked that our subclass, `eden_dirstate`, had to override quite a bit of behavior. Failing to manage the `.hg/dirstate` file in a way similar to the way Mercurial does has exposed some "unofficial contracts" that Mercurial has. For example, tools like Nuclide rely on changes to the `.hg/dirstate` file as a heuristic to determine when to invalidate its internal caches for Mercurial data. Today, Mercurial has a well-factored `dirstatemap` abstraction that is primarily responsible for the transactions with the dirstate's data. With this split, we can focus on putting most of our customizations in our `eden_dirstate_map` subclass while our `eden_dirstate` class has to override fewer methods. Because the data is managed through the `.hg/dirstate` file, transaction logic in Mercurial that relies on renaming/copying that file will work out-of-the-box. This change also reduces the number of Thrift calls the Mercurial extension has to make for operations like `hg status` or `hg add`. In this revision, we introduce our own binary format for the `.hg/dirstate` file. The logic to read and write this file is in `eden/py/dirstate.py`. After the first 40 bytes, which are used for the parent hashes, the next four bytes are reserved for a version number for the file format so we can manage file format changes going forward. Admittedly one downside of this change is that it is a breaking change. Ideally, users should commit all of their local changes in their existing mounts, shutdown Eden, delete the old mounts, restart Eden, and re-clone. In the end, this change deletes a number of Mercurial-specific code and Thrift APIs from Eden. This is a better separation of concerns that makes Eden more SCM-agnostic. For example, this change removes `Dirstate.cpp` and `DirstatePersistance.cpp`, replacing them with the much simpler and more general `Differ.cpp`. The Mercurial-specific logic from `Dirstate.cpp` that turned a diff into an `hg status` now lives in the Mercurial extension in `EdenThriftClient.getStatus()`, which is much more appropriate. Note that this reverts the changes that were recently introduced in D6116105: we now need to intercept `localrepo.localrepository.dirstate` once again. Reviewed By: simpkins Differential Revision: D6179950 fbshipit-source-id: 5b78904909b669c9cc606e2fe1fd118ef6eaab95
2017-11-07 06:44:24 +03:00
folly::Future<std::unique_ptr<ScmStatus>> future_getScmStatus(
Reimplement dirstate used by Eden's Hg extension as a subclass of Hg's dirstate. Summary: This is a major change to Eden's Hg extension. Our initial attempt to implement `edendirstate` was to create a "clean room" implementation that did not share code with `mercurial/dirstate.py`. This was helpful in uncovering the subset of the dirstate API that matters for Eden. It also provided a better safeguard against upstream changes to `dirstate.py` in Mercurial itself. In this implementation, the state transition management was mostly done on the server in `Dirstate.cpp`. We also made a modest attempt to make `Dirstate.cpp` "SCM-agnostic" such that the same APIs could be used for Git at some point. However, as we have tried to support more of the sophisticated functionality in Mercurial, particularly `hg histedit`, achieving parity between the clean room implementation and Mercurial's internals has become more challenging. Ultimately, the clean room implementation is likely the right way to go for Eden, but for now, we need to prioritize having feature parity with vanilla Hg when using Eden. Once we have a more complete set of integration tests in place, we can reimplement Eden's dirstate more aggressively to optimize things. Fortunately, the [[ https://bitbucket.org/facebook/hg-experimental/src/default/sqldirstate/ | sqldirstate ]] extension has already demonstrated that it is possible to provide a faithful dirstate implementation that subclasses the original `dirstate` while using a different storage mechanism. As such, I used `sqldirstate` as a model when implementing the new `eden_dirstate` (distinguishing it from our v1 implementation, `edendirstate`). In particular, `sqldirstate` uses SQL tables as storage for the following private fields of `dirstate`: `_map`, `_dirs`, `_copymap`, `_filefoldmap`, `_dirfoldmap`. Because `_filefoldmap` and `_dirfoldmap` exist to deal with case-insensitivity issues, we do not support them in `eden_dirstate` and add code to ensure the codepaths that would access them in `dirstate` never get exercised. Similarly, we also implemented `eden_dirstate` so that it never accesses `_dirs`. (`_dirs` is a multiset of all directories in the dirstate, which is an O(repo) data structure, so we do not want to maintain it in Eden. It appears to be primarily used for checking whether a path to a file already exists in the dirstate as a directory. We can protect against that in more efficient ways.) That leaves only `_map` and `_copymap` to worry about. `_copymap` contains the set of files that have been marked "copied" in the current dirstate, so it is fairly small and can be stored on disk or in memory with little concern. `_map` is a bit trickier because it is expected to have an entry for every file in the dirstate. In `sqldirstate`, it is stored across two tables: `files` and `nonnormalfiles`. For Eden, we already represent the data analogous to the `files` table in RocksDB/the overlay, so we do not need to create a new equivalent to the `files` table. We do, however, need an equivalent to the `nonnormalfiles` table, which we store in as Thrift-serialized data in an ordinary file along with the `_copymap` data. In our Hg extension, our implementation of `_map` is `eden_dirstate_map`, which is defined in a Python file of the same name. Our implementation of `_copymap` is `dummy_copymap`, which is defined in `eden_dirstate.py`. Both of these collections are simple pass-through data structures that translate their method calls to Thrift server calls. I expect we will want to optimize this in the future via some client-side caching, as well as creating batch APIs for talking to the server via Thrift. One advantage of this new implementation is that it enables us to delete `eden/hg/eden/overrides.py`, which overrode the entry points for `hg add` and `hg remove`. Between the recent implementation of `dirstate.walk()` for Eden and this switch to the real dirstate, we can now use the default implementation of `hg add` and `hg remove` (although we have to play some tricks, like in the implementation of `eden_dirstate.status()` in order to make `hg remove` work). In the course of doing this revision, I discovered that I had to make a minor fix to `EdenMatchInfo.make_glob_list()` because `hg add foo` was being treated as `hg add foo/**/*` even when `foo` was just a file (as opposed to a directory), in which case the glob was not matching `foo`! I also had to do some work in `eden_dirstate.status()` in which the `match` argument was previously largely ignored. It turns out that `dirstate.py` uses `status()` for a number of things with the `match` specified as a filter, so the output of `status()` must be filtered by `match` accordingly. Ultimately, this seems like work that would be better done on the server, but for simplicity, we're just going to do it in Python, for now. For the reasons explained above, this revision deletes a lot of code `Dirstate.cpp`. As such, `DirstateTest.cpp` does not seem worth refactoring, though the scenarios it was testing should probably be converted to integration tests. At a high level, the role of `DirstatePersistence` has not changed, but the exact data it writes is much different. Its corresponding unit test is also disabled, for now. Note that this revision does not change the name of the file where "dirstate data" is written (this is defined as `kDirstateFile` in `ClientConfig.cpp`), so we should blow away any existing instances of this file once this change lands. (It is still early enough in the project that it does not seem worth the overhead of a proper migration.) The true test of the success of this new approach is the ease with which we can write more integration tests for things like `hg histedit` and `hg graft`. Ideally, these should require very few changes to `eden_dirstate.py`. Reviewed By: simpkins Differential Revision: D5071778 fbshipit-source-id: e8fec4d393035d80f36516ac050cad025dc3ba31
2017-05-26 21:51:30 +03:00
std::unique_ptr<std::string> mountPoint,
Store Hg dirstate data in Hg instead of Eden. Summary: This is a major change to how we manage the dirstate in Eden's Hg extension. Previously, the dirstate information was stored under `$EDEN_CONFIG_DIR`, which is Eden's private storage. Any time the Mercurial extension wanted to read or write the dirstate, it had to make a Thrift request to Eden to do so on its behalf. The upside is that Eden could answer dirstate-related questions independently of the Python code. This was sufficiently different than how Mercurial's default dirstate worked that our subclass, `eden_dirstate`, had to override quite a bit of behavior. Failing to manage the `.hg/dirstate` file in a way similar to the way Mercurial does has exposed some "unofficial contracts" that Mercurial has. For example, tools like Nuclide rely on changes to the `.hg/dirstate` file as a heuristic to determine when to invalidate its internal caches for Mercurial data. Today, Mercurial has a well-factored `dirstatemap` abstraction that is primarily responsible for the transactions with the dirstate's data. With this split, we can focus on putting most of our customizations in our `eden_dirstate_map` subclass while our `eden_dirstate` class has to override fewer methods. Because the data is managed through the `.hg/dirstate` file, transaction logic in Mercurial that relies on renaming/copying that file will work out-of-the-box. This change also reduces the number of Thrift calls the Mercurial extension has to make for operations like `hg status` or `hg add`. In this revision, we introduce our own binary format for the `.hg/dirstate` file. The logic to read and write this file is in `eden/py/dirstate.py`. After the first 40 bytes, which are used for the parent hashes, the next four bytes are reserved for a version number for the file format so we can manage file format changes going forward. Admittedly one downside of this change is that it is a breaking change. Ideally, users should commit all of their local changes in their existing mounts, shutdown Eden, delete the old mounts, restart Eden, and re-clone. In the end, this change deletes a number of Mercurial-specific code and Thrift APIs from Eden. This is a better separation of concerns that makes Eden more SCM-agnostic. For example, this change removes `Dirstate.cpp` and `DirstatePersistance.cpp`, replacing them with the much simpler and more general `Differ.cpp`. The Mercurial-specific logic from `Dirstate.cpp` that turned a diff into an `hg status` now lives in the Mercurial extension in `EdenThriftClient.getStatus()`, which is much more appropriate. Note that this reverts the changes that were recently introduced in D6116105: we now need to intercept `localrepo.localrepository.dirstate` once again. Reviewed By: simpkins Differential Revision: D6179950 fbshipit-source-id: 5b78904909b669c9cc606e2fe1fd118ef6eaab95
2017-11-07 06:44:24 +03:00
bool listIgnored) override;
folly::Future<std::unique_ptr<ScmStatus>> future_getScmStatusBetweenRevisions(
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> oldHash,
std::unique_ptr<std::string> newHash) override;
void debugGetScmTree(
std::vector<ScmTreeEntry>& entries,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> id,
bool localStoreOnly) override;
void debugGetScmBlob(
std::string& data,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> id,
bool localStoreOnly) override;
void debugGetScmBlobMetadata(
ScmBlobMetadata& metadata,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> id,
bool localStoreOnly) override;
void debugInodeStatus(
std::vector<TreeInodeDebugInfo>& inodeInfo,
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> path) override;
void debugGetInodePath(
InodePathDebugInfo& inodePath,
std::unique_ptr<std::string> mountPoint,
int64_t inodeNumber) override;
void debugSetLogLevel(
SetLogLevelResult& result,
std::unique_ptr<std::string> category,
std::unique_ptr<std::string> level) override;
int64_t unloadInodeForPath(
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> path,
std::unique_ptr<TimeSpec> age) override;
void flushStatsNow() override;
void invalidateKernelInodeCache(
std::unique_ptr<std::string> mountPoint,
std::unique_ptr<std::string> path) override;
void getStatInfo(InternalStats& result) override;
/**
* When this Thrift handler is notified to shutdown, it notifies the
* EdenServer to shut down, as well.
*/
void shutdown() override;
private:
// Forbidden copy constructor and assignment operator
EdenServiceHandler(EdenServiceHandler const&) = delete;
EdenServiceHandler& operator=(EdenServiceHandler const&) = delete;
folly::Future<Hash> getSHA1ForPath(
folly::StringPiece mountPoint,
folly::StringPiece path);
folly::Future<Hash> getSHA1ForPathDefensively(
folly::StringPiece mountPoint,
folly::StringPiece path) noexcept;
Store Hg dirstate data in Hg instead of Eden. Summary: This is a major change to how we manage the dirstate in Eden's Hg extension. Previously, the dirstate information was stored under `$EDEN_CONFIG_DIR`, which is Eden's private storage. Any time the Mercurial extension wanted to read or write the dirstate, it had to make a Thrift request to Eden to do so on its behalf. The upside is that Eden could answer dirstate-related questions independently of the Python code. This was sufficiently different than how Mercurial's default dirstate worked that our subclass, `eden_dirstate`, had to override quite a bit of behavior. Failing to manage the `.hg/dirstate` file in a way similar to the way Mercurial does has exposed some "unofficial contracts" that Mercurial has. For example, tools like Nuclide rely on changes to the `.hg/dirstate` file as a heuristic to determine when to invalidate its internal caches for Mercurial data. Today, Mercurial has a well-factored `dirstatemap` abstraction that is primarily responsible for the transactions with the dirstate's data. With this split, we can focus on putting most of our customizations in our `eden_dirstate_map` subclass while our `eden_dirstate` class has to override fewer methods. Because the data is managed through the `.hg/dirstate` file, transaction logic in Mercurial that relies on renaming/copying that file will work out-of-the-box. This change also reduces the number of Thrift calls the Mercurial extension has to make for operations like `hg status` or `hg add`. In this revision, we introduce our own binary format for the `.hg/dirstate` file. The logic to read and write this file is in `eden/py/dirstate.py`. After the first 40 bytes, which are used for the parent hashes, the next four bytes are reserved for a version number for the file format so we can manage file format changes going forward. Admittedly one downside of this change is that it is a breaking change. Ideally, users should commit all of their local changes in their existing mounts, shutdown Eden, delete the old mounts, restart Eden, and re-clone. In the end, this change deletes a number of Mercurial-specific code and Thrift APIs from Eden. This is a better separation of concerns that makes Eden more SCM-agnostic. For example, this change removes `Dirstate.cpp` and `DirstatePersistance.cpp`, replacing them with the much simpler and more general `Differ.cpp`. The Mercurial-specific logic from `Dirstate.cpp` that turned a diff into an `hg status` now lives in the Mercurial extension in `EdenThriftClient.getStatus()`, which is much more appropriate. Note that this reverts the changes that were recently introduced in D6116105: we now need to intercept `localrepo.localrepository.dirstate` once again. Reviewed By: simpkins Differential Revision: D6179950 fbshipit-source-id: 5b78904909b669c9cc606e2fe1fd118ef6eaab95
2017-11-07 06:44:24 +03:00
/**
* If `filename` exists in the manifest as a file (not a directory), returns
* the mode of the file as recorded in the manifest.
*/
folly::Optional<mode_t> isInManifestAsFile(
const EdenMount* mount,
const RelativePathPiece filename);
EdenServer* const server_;
};
} // namespace eden
} // namespace facebook