Let's suppose I've got a branch structure in Git that looks like master -> A -> B
. That is, A
contains commits on top of master
and B
contains commits on top of A
.
I would like to reformat A
, without resulting in a massive set of conflicts in B
that will need to be resolved by hand. If I'm not mistaken, I could do this with git filter-branch, except I don't want anything in master
to change. (I.e. I don't want to invalidate all the public Git commit ID in the project.)
Is there a way to git filter-branch
on just A
and B
(or just as good for my purpose, all of the branches in the repo except master
)?
Edit: In particular, is it sufficient to do git filter-branch ... A B
? Will this avoid touching master
and keep A
and B
consistent with each other (i.e. rewrite the common commits between A
and B
to the same commit IDs and not allow them to be duplicated, or something like that).
You have the right general idea here (of how to use filter-branch), but the wrong syntax. To get git filter-branch
to copy (and filter) commits that are reachable from both names A
and B
but not to copy+filter commits that are reachable from master
, use the syntax:
git filter-branch <options> -- ^master A B
What we're doing here, with the git rev-list
arguments ^master A B
, is telling Git: Find all commits reachable from either A
or B
, excluding all commits reachable from master
. So if the commit chains go:
...--o--o--o <-- master
\
o--o--o <-- A
\
o--o <-- B
for instance, this selects the middle and bottom row commits. It would even with ^master B
or master..B
, as in phd's answer, but this is better for two reasons. First, it would still select all middle-row commits with:
...--o--o--o <-- master
\
o--o--o <-- A
\
o--o <-- B
(although this is presumably not your structure). Second, after copying (and filtering) the specified commits such that you wind up with:
o--o <-- [filtered copy of commit to which B points]
/
o--o--o [filtered copy of commit to which A points]
/
...--o--o--o <-- master
\
o--o--o <-- A
\
o--o <-- B
the filter-branch command will move all of what the documentation calls positive references. A positive reference is one that does not have a negation in front of it: in this case, A
and B
, but not master
, as master
is written ^master
and is therefore a negative reference.
Hence this won't touch master
(not that it would anyway—its commit did not get copied) but will yank the names A
and B
over so that they point to the copied commits.
Note that if you don't want changes made to the snapshots in the two (in this example) B
-only commits, it is up to you to write a clever filter that does that. (Or to use a filter that makes no tree changes, but by "reformat" I assume you want to use --tree-filter
and something like clang-format
on the snapshots in each of the various A
commits. It's probably more sensible to go ahead and make the same formatting changes to the various B
commits as well.)