git - How do I reword a commit (message) that is the parent of two branches? -
suppose have repo this
$ git log --oneline --graph * 3e0a28f merge branch 'other_branch' |\ | * d4fd67a add else | * d0f16bf merge branch 'master' other_branch | |\ | |/ |/| * | 3684fe5 make change in master | * 45b3ecb make change |/ * b2a9034 added text, reword me * 7b1ac57 initial commit
d0f16bf
involves fixing merge conflicts. (3684fe5
, 45b3ecb
modify same line). want reword b2a9034
$ git rebase -i b2a9034^ [detached head a9bd978] added text, reworded 1 file changed, 2 insertions(+) error: not apply 3684fe5... make change in master when have resolved problem, run "git rebase --continue". if prefer skip patch, run "git rebase --skip" instead. check out original branch , stop rebasing, run "git rebase --abort". not apply 3684fe570517de37e1ce7661e3821372e1eee967... make change in master
is there way reword b2a9034
without fixing merge conflicts again?
when "reword" assume mean "change commit message, not committed files" (the git rebase -i
meaning).
git's "swiss-army chainsaw" command, git filter-branch
, has option this. (of course, filter-branch
difficult use, hence "swiss-army chainsaw" appellation.)
specifically, filter-branch has --msg-filter
, allows copying commits while making changes message:
this filter rewriting commit messages. argument evaluated in shell original commit message on standard input; standard output used new commit message.
what means can keep existing message commits not rewrite target, , edit or replace 1 target, using suitable --msg-filter
, such as:
git filter-branch ... \ --msg-filter 'if [ $git_commit == b2a9034... ]; \ cat $home/new_msg; else cat; fi' \ ...
this filter copies message as-is (cat
) unless it's 1 targeted commit, in case ignores stdin message , prints instead contents of prepared-in-advance $home/new_msg
file. can of course write valid shell script instead, sure preserve exact original message of other commits.
you need fill in full commit id here (or use prefix matching it's wiser fill in full id). full id partial one, easiest method use git rev-parse
:
$ git rev-parse b2a9034 b2a9034... <-- full 40 char sha-1 comes out $
you need fill in rest of ...
parts git filter-branch
, nontrivial.
since filter-branch slow, want limit smallish number of commits. can git rev-list
arguments: filter-branch pass them on git rev-list
, copy commits listed. thus, can test out first:
$ git rev-list ^b2a9034^ branch1 branch2
here 2 branch
es names of branches branch-tips want rewritten (probably 1 of them master
, based on text above). first argument, ^b2a9034^
, should cause git rev-list
omit parent commit of b2a9034
, earlier commits. (the first ^
character "not" operator git rev-list
, second parent-following operator of gitrevisions
. can bit confusing alternative spelling ^b2a9034~1
, has same meaning, doesn't use ^
in 2 different ways. i'm not sure how less confusing in end, though.)
(if repository has few commits, rev-limiting not important.)
finally, note that, filter-branch documentation says:
the command rewrite positive refs mentioned in command line (e.g. if pass
a..b
,b
rewritten). ...
what phrase means not obvious me until understood git's internals, , there, how filter-branch does. internally, git ever adds things, git filter-branch
copies existing commits new ones. if new commit same original commit, copy bit-for-bit identical original, original sha-1 copy, meaning nothing added , nothing changes. if change anything, though, new, different sha-1 id.
this means filter branch runs along copying commits, "copies" commits prior 1 being modified , gets original sha-1 again. hits first (and maybe only) 1 want changed, , gets new sha-1. original commit remains in repository there's new copy new sha-1.
once that's happened, subsequent commits filter-branch copies have @ least 1 change made, if none of filters change them. in particular, have (at least 1 of) parent id(s), new id. first new child commit has new parent id gets new id too; child commit has pick new id; , on.
the end result new copies of commits give new chain of commit-ids ending new commit id (in case two) branch(es) you're filtering. git must save ids new branch-tips. when documentation says "only b
rewritten", means filter-branch
update refs/heads/b
—the file holds id of tip of branch b
—to have final sha-1 of copied branch-tip.
thus, listing, say, ^b2a9034^ master develop
, you've provided 2 "positive refs", namely refs/heads/master
, refs/heads/develop
, , 2 filter-branch update. ^b2a9034^
"negative ref" ("exclude b2a9034^
, earlier) , hence filter-branch nothing after passing git rev-list
.
Comments
Post a Comment