mirror of
https://github.com/facebook/sapling.git
synced 2024-10-08 07:49:11 +03:00
b4f3c70c6a
Summary: Historically, we have seen a number of messages like the following in the Eden logs: ``` Journal for .hg/blackbox.log holds invalid Created, Created sequence ``` Apparently we were getting these invalid sequences because we were not always recording a "rename" correctly. The "rename" constructor for a `JournalDelta` assumed that the source path should be included in the list of "removed" files while the destination path should be included in the list of "created" files. However, that is not accurate if the destination path already existed before the user ran `mv`. Fortunately, we already check whether the destination file exists in `TreeInode::doRename()`, so it is straightforward to determine whether the action is a "rename" (destination does not exist) or an "replace" (destination already exists) and then classify the destination path accordingly. As demonstrated by the new test introduced in this commit (`JournalUpdateTest::moveFileReplace`), in the old implementation, a file that was removed after it was overwritten would not show up as removed in the merged `JournalDelta`. Because Watchman relies on `JournalDelta::merge()` via the Thrift method `getFilesChangedSince()`, this would cause Watchman to report such a file as still existing even though it was removed. This definitely caused bugs in Nuclide. It is likely that other tools that rely on Watchman in Eden (such as Buck) may have also done incorrect things because of this bug, so this could explain past reported issues. Reviewed By: simpkins Differential Revision: D7888249 fbshipit-source-id: 3e57963f27c5421a6175d1a759db8d9597ed76f3
88 lines
3.3 KiB
C++
88 lines
3.3 KiB
C++
/*
|
|
* 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 "Journal.h"
|
|
|
|
#include <chrono>
|
|
#include <unordered_set>
|
|
#include "eden/fs/model/Hash.h"
|
|
#include "eden/fs/utils/PathFuncs.h"
|
|
|
|
namespace facebook {
|
|
namespace eden {
|
|
|
|
class JournalDelta {
|
|
public:
|
|
enum Created { CREATED };
|
|
enum Removed { REMOVED };
|
|
enum Renamed { RENAME };
|
|
enum Replaced { REPLACE };
|
|
JournalDelta() = default;
|
|
JournalDelta(JournalDelta&&) = default;
|
|
JournalDelta& operator=(JournalDelta&&) = default;
|
|
JournalDelta(const JournalDelta&) = delete;
|
|
JournalDelta& operator=(const JournalDelta&) = delete;
|
|
JournalDelta(std::initializer_list<RelativePath> overlayFileNames);
|
|
JournalDelta(RelativePathPiece fileName, Created);
|
|
JournalDelta(RelativePathPiece fileName, Removed);
|
|
/**
|
|
* "Renamed" means that that newName was created as a result of the mv(1).
|
|
*/
|
|
JournalDelta(RelativePathPiece oldName, RelativePathPiece newName, Renamed);
|
|
|
|
/**
|
|
* "Replaced" means that that newName was overwritten by oldName as a result
|
|
* of the mv(1).
|
|
*/
|
|
JournalDelta(RelativePathPiece oldName, RelativePathPiece newName, Replaced);
|
|
|
|
/** the prior delta and its chain */
|
|
std::shared_ptr<const JournalDelta> previous;
|
|
/** The current sequence range.
|
|
* This is a range to accommodate merging a range into a single entry. */
|
|
Journal::SequenceNumber fromSequence;
|
|
Journal::SequenceNumber toSequence;
|
|
/** The time at which the change was recorded.
|
|
* This is a range to accommodate merging a range into a single entry. */
|
|
std::chrono::steady_clock::time_point fromTime;
|
|
std::chrono::steady_clock::time_point toTime;
|
|
|
|
/** The snapshot hash that we started and ended up on.
|
|
* This will often be the same unless we perform a checkout or make
|
|
* a new snapshot from the snapshotable files in the overlay. */
|
|
Hash fromHash;
|
|
Hash toHash;
|
|
|
|
/** The set of files that changed in the overlay in this update */
|
|
std::unordered_set<RelativePath> changedFilesInOverlay;
|
|
/** The set of files that were created in the overlay in this update */
|
|
std::unordered_set<RelativePath> createdFilesInOverlay;
|
|
/** The set of files that were removed in the overlay in this update */
|
|
std::unordered_set<RelativePath> removedFilesInOverlay;
|
|
/** The set of files that had differing status across a checkout or
|
|
* some other operation that changes the snapshot hash */
|
|
std::unordered_set<RelativePath> uncleanPaths;
|
|
|
|
/** Merge the deltas running back from this delta for all deltas
|
|
* whose toSequence is >= limitSequence.
|
|
* The default limit value is 0 which is never assigned by the Journal
|
|
* and thus indicates that all deltas should be merged.
|
|
* if pruneAfterLimit is true and we stop due to hitting limitSequence,
|
|
* then the returned delta will have previous=nullptr rather than
|
|
* maintaining the chain.
|
|
* If the limitSequence means that no deltas will match, returns nullptr.
|
|
* */
|
|
std::unique_ptr<JournalDelta> merge(
|
|
Journal::SequenceNumber limitSequence = 0,
|
|
bool pruneAfterLimit = false) const;
|
|
};
|
|
} // namespace eden
|
|
} // namespace facebook
|