Skip to content

Commit 9619de7

Browse files
YousefSalamameta-codesync[bot]
authored andcommitted
monad: add delete-directory subcommand
Summary: It was convenient to have an admin command for creating a bonsai changeset that deletes a directory with SEV fighting S646665. Let's land this for similar such SEVs. Reviewed By: gustavoavena, clara-9 Differential Revision: D100177683 fbshipit-source-id: 00d3fe426a92be3e9e3122a8897a721307663f9b
1 parent 407c87b commit 9619de7

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This software may be used and distributed according to the terms of the
4+
# GNU General Public License found in the LICENSE file in the root
5+
# directory of this source tree.
6+
7+
$ . "${TEST_FIXTURES}/library.sh"
8+
9+
setup configuration
10+
$ setup_common_config
11+
12+
$ testtool_drawdag -R repo --derive-all << 'EOF' 2>/dev/null
13+
> A
14+
> # modify: A "dir/subdir/file1" "content1"
15+
> # modify: A "dir/subdir/file2" "content2"
16+
> # modify: A "dir/subdir/nested/file3" "content3"
17+
> # modify: A "dir/other_file" "other"
18+
> # modify: A "root_file" "root"
19+
> # bookmark: A main
20+
> EOF
21+
22+
Test 1: Basic directory deletion
23+
$ mononoke_admin commit -R repo delete-directory --parent $A --path dir/subdir | tee $TESTTMP/delete_output
24+
Deleting 3 files under 'dir/subdir'
25+
* (glob)
26+
27+
$ DELETE_CS_ID=$(tail -1 $TESTTMP/delete_output)
28+
29+
Verify the deletion commit
30+
$ mononoke_admin fetch -R repo -i "$DELETE_CS_ID"
31+
BonsaiChangesetId: * (glob)
32+
Author: svcscm
33+
Message: Deleted via mononoke_admin commit delete-directory
34+
FileChanges:
35+
REMOVED: dir/subdir/file1
36+
REMOVED: dir/subdir/file2
37+
REMOVED: dir/subdir/nested/file3
38+
39+
40+
Test 2: Error case - non-existent path
41+
$ mononoke_admin commit -R repo delete-directory --parent $A --path nonexistent
42+
Error: Path 'nonexistent' does not exist in the given commit
43+
[1]
44+
45+
Test 3: Error case - path is a file, not a directory
46+
$ mononoke_admin commit -R repo delete-directory --parent $A --path root_file
47+
Error: Path 'root_file' is a file, not a directory
48+
[1]

eden/mononoke/tools/admin/src/commands/commit.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* GNU General Public License version 2.
66
*/
77

8+
mod delete_directory;
89
mod pushrebase;
910
mod rebase;
1011
mod split;
@@ -37,6 +38,7 @@ use repo_cross_repo::RepoCrossRepo;
3738
use repo_derived_data::RepoDerivedData;
3839
use repo_identity::RepoIdentity;
3940

41+
use self::delete_directory::CommitDeleteDirectoryArgs;
4042
use self::pushrebase::CommitPushrebaseArgs;
4143
use self::rebase::CommitRebaseArgs;
4244
use self::split::CommitSplitArgs;
@@ -124,6 +126,9 @@ pub enum CommitSubcommand {
124126
/// Rebases a commit from its current bookmark onto a bookmark, and moves
125127
/// that bookmark to the newly rebased commit.
126128
Pushrebase(CommitPushrebaseArgs),
129+
130+
/// Delete all files under a directory, creating a new commit
131+
DeleteDirectory(CommitDeleteDirectoryArgs),
127132
}
128133

129134
pub async fn run(app: MononokeApp, args: CommandArgs) -> Result<()> {
@@ -140,6 +145,9 @@ pub async fn run(app: MononokeApp, args: CommandArgs) -> Result<()> {
140145
CommitSubcommand::Pushrebase(pushrebase_args) => {
141146
pushrebase::pushrebase(&ctx, &repo, pushrebase_args).await?
142147
}
148+
CommitSubcommand::DeleteDirectory(args) => {
149+
delete_directory::delete_directory(&ctx, &repo, args).await?
150+
}
143151
}
144152

145153
Ok(())
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This software may be used and distributed according to the terms of the
5+
* GNU General Public License version 2.
6+
*/
7+
8+
use anyhow::Result;
9+
use anyhow::bail;
10+
use changesets_creation::save_changesets;
11+
use clap::Args;
12+
use commit_id::parse_commit_id;
13+
use context::CoreContext;
14+
use derivation_queue_thrift::DerivationPriority;
15+
use futures::TryStreamExt;
16+
use manifest::Entry;
17+
use manifest::ManifestOps;
18+
use mononoke_types::BonsaiChangesetMut;
19+
use mononoke_types::DateTime;
20+
use mononoke_types::FileChange;
21+
use mononoke_types::NonRootMPath;
22+
use repo_blobstore::RepoBlobstoreRef;
23+
use repo_derived_data::RepoDerivedDataRef;
24+
use skeleton_manifest::RootSkeletonManifestId;
25+
use sorted_vector_map::SortedVectorMap;
26+
27+
use super::Repo;
28+
29+
#[derive(Args)]
30+
pub struct CommitDeleteDirectoryArgs {
31+
/// Commit ID of the parent commit (e.g. bookmark name, bonsai hash)
32+
#[clap(long)]
33+
parent: String,
34+
35+
/// Path of the directory to delete
36+
#[clap(long)]
37+
path: String,
38+
39+
/// Commit message for the deletion commit
40+
#[clap(
41+
long,
42+
default_value = "Deleted via mononoke_admin commit delete-directory"
43+
)]
44+
message: String,
45+
46+
/// Author of the deletion commit
47+
#[clap(long, default_value = "svcscm")]
48+
author: String,
49+
}
50+
51+
pub async fn delete_directory(
52+
ctx: &CoreContext,
53+
repo: &Repo,
54+
args: CommitDeleteDirectoryArgs,
55+
) -> Result<()> {
56+
let parent_cs_id = parse_commit_id(ctx, repo, &args.parent).await?;
57+
58+
// Derive skeleton manifest for parent
59+
let root_sk_id = repo
60+
.repo_derived_data()
61+
.derive::<RootSkeletonManifestId>(ctx, parent_cs_id, DerivationPriority::LOW)
62+
.await?
63+
.into_skeleton_manifest_id();
64+
65+
let dir_path = NonRootMPath::new(&args.path)?;
66+
67+
// Find the directory entry in the skeleton manifest
68+
let entry = root_sk_id
69+
.find_entry(
70+
ctx.clone(),
71+
repo.repo_blobstore().clone(),
72+
dir_path.clone().into(),
73+
)
74+
.await?;
75+
76+
let tree_id = match entry {
77+
Some(Entry::Tree(tree_id)) => tree_id,
78+
Some(Entry::Leaf(_)) => bail!("Path '{}' is a file, not a directory", args.path),
79+
None => bail!("Path '{}' does not exist in the given commit", args.path),
80+
};
81+
82+
// List all files under the directory
83+
let files: Vec<NonRootMPath> = tree_id
84+
.list_leaf_entries(ctx.clone(), repo.repo_blobstore().clone())
85+
.map_ok(|(relative_path, ())| dir_path.join(&relative_path))
86+
.try_collect()
87+
.await?;
88+
89+
if files.is_empty() {
90+
bail!("No files found under '{}'", args.path);
91+
}
92+
93+
println!("Deleting {} files under '{}'", files.len(), args.path);
94+
95+
// Build deletion file changes
96+
let file_changes: SortedVectorMap<NonRootMPath, FileChange> = files
97+
.into_iter()
98+
.map(|path| (path, FileChange::Deletion))
99+
.collect();
100+
101+
// Create the deletion commit
102+
let bcs = BonsaiChangesetMut {
103+
parents: vec![parent_cs_id],
104+
author: args.author,
105+
author_date: DateTime::now(),
106+
message: args.message,
107+
file_changes,
108+
..Default::default()
109+
}
110+
.freeze()?;
111+
112+
let cs_id = bcs.get_changeset_id();
113+
save_changesets(ctx, repo, vec![bcs]).await?;
114+
115+
println!("{}", cs_id);
116+
117+
Ok(())
118+
}

0 commit comments

Comments
 (0)