Skip to content

Commit af0efe1

Browse files
Add overwriting of author to squash filter
Change: squash-author
1 parent 6ec80cf commit af0efe1

File tree

4 files changed

+84
-43
lines changed

4 files changed

+84
-43
lines changed

src/bin/josh-filter.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,20 @@ fn make_app() -> clap::Command {
3939
clap::Arg::new("squash")
4040
.help("Produce a history that contains only commits pointed to by references matching the given pattern")
4141
.long("squash")
42-
.takes_value(true),
42+
)
43+
.arg(
44+
clap::Arg::new("author")
45+
.help("Author to use for commits with rewritten message")
46+
.long("author")
47+
)
48+
.arg(
49+
clap::Arg::new("email")
50+
.help("Author email to use for commits with rewritten message")
51+
.long("email")
4352
)
4453
.arg(
4554
clap::Arg::new("single")
55+
.action(clap::ArgAction::SetTrue)
4656
.help("Produce a history that contains only one single commit")
4757
.long("single"),
4858
)
@@ -143,8 +153,8 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
143153

144154
let mut filterobj = josh::filter::parse(&specstr)?;
145155

146-
if args.is_present("print-filter") {
147-
let filterobj = if args.is_present("reverse") {
156+
if args.get_flag("print-filter") {
157+
let filterobj = if args.get_flag("reverse") {
148158
josh::filter::invert(filterobj)?
149159
} else {
150160
filterobj
@@ -163,7 +173,7 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
163173
let transaction = josh::cache::Transaction::new(repo, None);
164174
let repo = transaction.repo();
165175

166-
let input_ref = args.value_of("input").unwrap();
176+
let input_ref = args.get_one::<String>("input").unwrap();
167177

168178
let mut refs = vec![];
169179
let mut ids = vec![];
@@ -172,11 +182,11 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
172182
let input_ref = reference.name().unwrap().to_string();
173183
refs.push((input_ref.clone(), reference.target().unwrap()));
174184

175-
if args.is_present("single") {
185+
if args.get_flag("single") {
176186
filterobj = josh::filter::chain(josh::filter::squash(None), filterobj);
177187
}
178188

179-
if let Some(pattern) = args.value_of("squash") {
189+
if let Some(pattern) = args.get_one::<String>("squash") {
180190
let pattern = pattern.to_string();
181191
for reference in repo.references_glob(&pattern).unwrap() {
182192
let reference = reference?;
@@ -185,7 +195,14 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
185195
refs.push((reference.name().unwrap().to_string(), target));
186196
}
187197
}
188-
filterobj = josh::filter::chain(josh::filter::squash(Some(&ids)), filterobj);
198+
filterobj = josh::filter::chain(
199+
josh::filter::squash(Some((
200+
args.get_one::<String>("author").unwrap(),
201+
args.get_one::<String>("email").unwrap(),
202+
&ids,
203+
))),
204+
filterobj,
205+
);
189206
};
190207

191208
let odb = repo.odb()?;
@@ -214,7 +231,7 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
214231
}
215232
});
216233

217-
if args.is_present("discover") {
234+
if args.get_flag("discover") {
218235
let r = repo.revparse_single(&input_ref)?;
219236
let hs = josh::housekeeping::find_all_workspaces_and_subdirectories(&r.peel_to_tree()?)?;
220237
for i in hs {
@@ -238,7 +255,7 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
238255

239256
let reverse = args.get_flag("reverse");
240257

241-
let check_permissions = args.is_present("check-permission");
258+
let check_permissions = args.get_flag("check-permission");
242259
let mut permissions_filter = josh::filter::empty();
243260
if check_permissions {
244261
let whitelist;
@@ -361,17 +378,17 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
361378
}
362379

363380
if !reverse
364-
&& args.value_of("update") != Some("FILTERED_HEAD")
381+
&& args.get_one::<String>("update") != Some(&"FILTERED_HEAD".to_string())
365382
&& updated_refs.len() == 1
366383
&& updated_refs[0].1 == old_oid
367384
{
368385
println!(
369386
"Warning: reference {} wasn't updated",
370-
args.value_of("update").unwrap()
387+
args.get_one::<String>("update").unwrap()
371388
);
372389
}
373390

374-
if let Some(gql_query) = args.value_of("graphql") {
391+
if let Some(gql_query) = args.get_one::<String>("graphql") {
375392
let context = josh::graphql::context(transaction.try_clone()?, transaction.try_clone()?);
376393
*context.allow_refs.lock()? = true;
377394
let (res, _errors) = juniper::execute_sync(

src/filter/mod.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,12 @@ pub fn empty() -> Filter {
6161
to_filter(Op::Empty)
6262
}
6363

64-
pub fn squash(ids: Option<&[(git2::Oid, String)]>) -> Filter {
65-
if let Some(ids) = ids {
64+
pub fn squash(ids: Option<(&str, &str, &[(git2::Oid, String)])>) -> Filter {
65+
if let Some((author, email, ids)) = ids {
6666
to_filter(Op::Squash(Some(
67-
ids.iter().map(|(x, y)| (*x, y.clone())).collect(),
67+
ids.iter()
68+
.map(|(x, y)| (*x, (y.clone(), author.to_string(), email.to_string())))
69+
.collect(),
6870
)))
6971
} else {
7072
to_filter(Op::Squash(None))
@@ -95,7 +97,7 @@ enum Op {
9597
Empty,
9698
Fold,
9799
Paths,
98-
Squash(Option<std::collections::HashMap<git2::Oid, String>>),
100+
Squash(Option<std::collections::HashMap<git2::Oid, (String, String, String)>>),
99101
Linear,
100102

101103
RegexReplace(regex::Regex, String),
@@ -250,7 +252,7 @@ fn spec2(op: &Op) -> String {
250252
Op::Squash(Some(hs)) => {
251253
let mut v = hs
252254
.iter()
253-
.map(|(x, y)| format!("{}:{}", x, y))
255+
.map(|(x, y)| format!("{}:{}:{}:{}", x, y.0, y.1, y.2))
254256
.collect::<Vec<String>>();
255257
v.sort();
256258
let s = v.join(",");

src/history.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +181,29 @@ pub fn rewrite_commit(
181181
base: &git2::Commit,
182182
parents: &[&git2::Commit],
183183
tree: &git2::Tree,
184-
message: Option<String>,
184+
message: Option<(String, String, String)>,
185185
) -> JoshResult<git2::Oid> {
186186
if message == None && base.tree()?.id() == tree.id() && all_equal(base.parents(), parents) {
187187
// Looks like an optimization, but in fact serves to not change the commit in case
188188
// it was signed.
189189
return Ok(base.id());
190190
}
191191

192-
let b = repo.commit_create_buffer(
193-
&base.author(),
194-
&base.committer(),
195-
&message.unwrap_or(base.message_raw().unwrap_or("no message").to_string()),
196-
tree,
197-
parents,
198-
)?;
192+
let b = if let Some((message, author, email)) = message {
193+
let a = base.author();
194+
let new_a = git2::Signature::new(&author, &email, &a.when())?;
195+
let c = base.committer();
196+
let new_c = git2::Signature::new(&author, &email, &c.when())?;
197+
repo.commit_create_buffer(&new_a, &new_c, &message, tree, parents)?
198+
} else {
199+
repo.commit_create_buffer(
200+
&base.author(),
201+
&base.committer(),
202+
&base.message_raw().unwrap_or("no message"),
203+
tree,
204+
parents,
205+
)?
206+
};
199207

200208
if let Ok((sig, _)) = repo.extract_signature(&base.id(), None) {
201209
// Re-create the object with the original signature (which of course does not match any
@@ -551,7 +559,7 @@ pub fn create_filtered_commit<'a>(
551559
filtered_tree: git2::Tree<'a>,
552560
transaction: &cache::Transaction,
553561
filter: filter::Filter,
554-
message: Option<String>,
562+
message: Option<(String, String, String)>,
555563
) -> JoshResult<git2::Oid> {
556564
let (r, is_new) = create_filtered_commit2(
557565
transaction.repo(),
@@ -573,7 +581,7 @@ fn create_filtered_commit2<'a>(
573581
original_commit: &'a git2::Commit,
574582
filtered_parent_ids: Vec<git2::Oid>,
575583
filtered_tree: git2::Tree<'a>,
576-
message: Option<String>,
584+
message: Option<(String, String, String)>,
577585
) -> JoshResult<(git2::Oid, bool)> {
578586
let filtered_parent_commits: Result<Vec<_>, _> = filtered_parent_ids
579587
.iter()

tests/filter/squash.t

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
$ git merge -q branch2 --no-ff
3030

31-
$ josh-filter -s --squash "refs/tags/*" --update refs/heads/filtered
31+
$ josh-filter -s --squash "refs/tags/*" --author "New Author" --email "new@e.mail" --update refs/heads/filtered
3232
Warning: reference refs/heads/filtered wasn't updated
3333
[1] :SQUASH=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
3434

@@ -38,14 +38,15 @@
3838
'git <command> [<revision>...] -- [<file>...]'
3939
[128]
4040
$ git tag tag_a 1d69b7d
41-
$ josh-filter -s --squash "refs/tags/*" --update refs/heads/filtered
41+
$ josh-filter -s --squash "refs/tags/*" --author "New Author" --email "new@e.mail" --update refs/heads/filtered
4242
[1] :SQUASH=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
43-
[2] :SQUASH=10d465cdf297e8062eed54204414414faa63671e
43+
[2] :SQUASH=e8e83b9c5d2f779f0cea83a6cad68b710a399c96
4444

4545
$ git log --graph --decorate --pretty=oneline refs/heads/filtered
46-
* 97a9ff7bd4dad25b9dacdfdaeb861e74e7b4aef8 (tag: filtered/tag_a, filtered) refs/tags/tag_a
46+
* d8aa5a9937f4f0bd645dbc0b591bae5cd6b6d91b (tag: filtered/tag_a, filtered) refs/tags/tag_a
4747
$ git tag tag_b 0b4cf6c
4848

49+
4950
$ git log --graph --decorate --pretty=oneline
5051
* 1d69b7d2651f744be3416f2ad526aeccefb99310 (HEAD -> master, tag: tag_a) Merge branch 'branch2'
5152
|\
@@ -55,28 +56,41 @@
5556
|/
5657
* 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb (tag: tag_b) add file1
5758

58-
$ josh-filter -s --squash "refs/tags/*" --update refs/heads/filtered
59+
$ josh-filter -s --squash "refs/tags/*" --author "New Author" --email "new@e.mail" --update refs/heads/filtered
5960
[1] :SQUASH=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
60-
[2] :SQUASH=10d465cdf297e8062eed54204414414faa63671e
61-
[3] :SQUASH=1683a7fc84387b56a1a5de8e9fcf720166951949
61+
[2] :SQUASH=e8e83b9c5d2f779f0cea83a6cad68b710a399c96
62+
[3] :SQUASH=3953063f3dc58661e9db16f9014aab1e8ec50bf8
6263

6364
$ git log --graph --decorate --pretty=oneline refs/heads/filtered
64-
* fa3bd3f0d90d5894c6ac402ef5f764b75335ec01 (tag: filtered/tag_a, filtered) refs/tags/tag_a
65+
* 5b1a753860ca124024f6dfb4fd018fe7df8beae4 (tag: filtered/tag_a, filtered) refs/tags/tag_a
66+
|\
67+
* 96a731a4d64a8928e6af7abb2d425df3812b4197 (tag: filtered/tag_b) refs/tags/tag_b
68+
69+
$ git log --graph --pretty=%an:%ae refs/heads/master
70+
* Josh:josh@example.com
71+
|\
72+
| * Josh:josh@example.com
73+
| * Josh:josh@example.com
74+
* | Josh:josh@example.com
75+
|/
76+
* Josh:josh@example.com
77+
$ git log --graph --pretty=%an:%ae refs/heads/filtered
78+
* New Author:new@e.mail
6579
|\
66-
* 077b2cad7b3fbc393b6320b90c9c0be1255ac309 (tag: filtered/tag_b) refs/tags/tag_b
80+
* New Author:new@e.mail
6781

6882
$ git tag tag_c 975d4c4
6983

70-
$ josh-filter -s --squash "refs/tags/*" --update refs/heads/filtered
84+
$ josh-filter -s --squash "refs/tags/*" --author "New Author" --email "new@e.mail" --update refs/heads/filtered
7185
[1] :SQUASH=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
72-
[2] :SQUASH=10d465cdf297e8062eed54204414414faa63671e
73-
[3] :SQUASH=1683a7fc84387b56a1a5de8e9fcf720166951949
74-
[6] :SQUASH=06a82cb9d2d3abb0ac59f8c782fd7edecc8e8d28
86+
[2] :SQUASH=e8e83b9c5d2f779f0cea83a6cad68b710a399c96
87+
[3] :SQUASH=3953063f3dc58661e9db16f9014aab1e8ec50bf8
88+
[6] :SQUASH=6a132477d438779dbaeb0d68b9aab55786e28dd9
7589

7690
$ git log --graph --decorate --pretty=oneline refs/heads/filtered
77-
* dc1dc0211db7a1aea1234af950b4946afa5a6f14 (tag: filtered/tag_a, filtered) refs/tags/tag_a
91+
* 9fe45cb2bead844630852ab338ecd8e073f8ba50 (tag: filtered/tag_a, filtered) refs/tags/tag_a
7892
|\
79-
| * 500760f4e4f3d4ba6e73af7ce0a98d91a25a503a (tag: filtered/tag_c) refs/tags/tag_c
93+
| * d6b88d4c1cc566b7f4d9b51353ec6f3204a93b81 (tag: filtered/tag_c) refs/tags/tag_c
8094
|/
81-
* 077b2cad7b3fbc393b6320b90c9c0be1255ac309 (tag: filtered/tag_b) refs/tags/tag_b
95+
* 96a731a4d64a8928e6af7abb2d425df3812b4197 (tag: filtered/tag_b) refs/tags/tag_b
8296

0 commit comments

Comments
 (0)