I cannot figure out how to update a branch to be identical to another branch. Here's an example:
git init test; cd test
echo apple >a; echo banana >b; git add a b; git commit -m 'A1/a:apple;b:banana'
echo carrot >c; git add c; git commit -m 'A2/c:carrot'
git checkout -b second HEAD^1
echo beets >b; echo dandelion >d; git add b d; git commit -m 'B1/b:beets;d:dandelion'
At this point my history looks like this:
A1-----A2 (master, contains a:apple, b:banana, c:carrot)
\
\----B1 (second, contains a:apple, b:beets, d:dandelion)
Now I'd like to add a commit to the "second" branch to make its contents match the "master" branch, that is:
A1-----A2
\
\----B1--?--B2 (desired contents a:apple, b:banana, c:carrot)
My question is, what git commands do I run to do this? The closest I've come up with is:
git checkout master .
git commit -m B2
However, while this resets b to banana and restores c, it does not remove the file d. I have not been able to come up with any variation of git reset
that does what I want either. I don't think I want to do a version of git revert
, because in my actual repository the history on the branches is more complicated than this example and the branches may contain merges.
Any help will be greatly appreciated!
I assume current branch is second
. If you unhappy with git reset --HARD master
(because it will require force push if the second
is published anywhere), you could undo B1 by git revert B1
(or just git revert master..second
if you don't want to list all commits such as B1) and then git merge master
.
Using @StevenPenny idea with ours
strategy I came up with this solution:
git checkout master
git checkout -b transfer_second_to_master
git merge -s ours second
git checkout second
git merge transfer_second_to_master
git branch -d transfer_second_to_master
It is kind of emulation of theirs
strategy, done as inversion of the ours
stategy. And now git diff master second
gives nothing, it means branches are identical.
You almost had the right answer. The solution is to first remove all of the files before checking out from master
.
git checkout second
git rm -rf .
git checkout master .
git commit -m B2
This will create a new commit with a new message, author, and timestamp. If you all that to be the same, your best bet is...
Download git-reparent, install it in your $PATH
, and then
git checkout second
git branch parent
git reset --hard master
git reparent -p parent
git branch -d parent
Explanation:
second
).parent
) to the current commit.master
).second
) to have the contents of the current commit but have the given parents (parent
).parent
).This sounds like a job for
git merge
git checkout master
git merge -s ours second
more info
This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches.
Before
A1-----A2 (master, contains a:apple, b:banana, c:carrot)
\
\----B1 (second, contains a:apple, b:beets, d:dandelion)
git status
: cleangit read-tree -m -u master
git commit -m 'B2: copy of A2'
git diff master
After:
A1-----A2 (master, contains a:apple, b:banana, c:carrot)
\
\----B1---B2 (second, contains a:apple, b:banana, c:carrot)
git status
: cleandiff
status to master
: cleanConfirm backing off works:
git reset --hard HEAD^
git clean -f
git branch -avv
Shows the before state again.
What does this variant do?
git read-tree
reads the given other commit (here master
) into INDEX-u
is given, it updates worktree accordinglySorry, I did not find out the correct way. Here is a workaround:
First: Do a dummy merge
git merge -s ours -m 'B2 dummy' master
Second: Fix the merge to the correct data
git read-tree -m -u master
git commit --amend -m 'B2: copy of A2'
Third: Check that it really matches master
:
git diff master
After:
A1-------A2 (master, contains a:apple, b:banana, c:carrot)
\ \
\----B1---B2 (second, contains a:apple, b:banana, c:carrot)
git status
: cleandiff
status to master
: cleanConfirm backing off works:
git reset --hard HEAD^
git clean -f
git branch -avv
Shows the before state again, as it should.
How does this work:
git read-tree -m -u master
resets the merge information, so we cannot start a merge, read the tree and then commit this merge afterwards, sadly.-u
, as this is crucial for updating the worktree. But -u
requires -m
or similar which throws us out of merge mode.ours
to prevent merge conflicts, which is exactly the wrong thing we want to happen).git read-tree
as we wantedamend
the merge with the now, finally correct, index contents.ff
according to upstream.What is the problem with this?
git commit
is meant to be atomic. Either you have a correct state or not.Other Solutions to do it seem to be far too complicated. But perhaps I oversaw a bit?
Either use completely incomprehensible git-plumbing commands to create the correct commit-object yourself -- a no go for a solution which shall be simple to understand and be used.
Or do some very complex sub-branching and then fast-forward to the sub-branch, then delete it -- a double no-go, as this even introduces way more complicated things like need for complex cleanup if something breaks.
Or find out which commands are needed to update the worktree to the index (which re-invents the wheel as -u
can already do it) after a git read-tree master
, as this should happen before(!) the commit (as usual).
This has not yet been thoroughly tested with submodules. If they are added, deleted, replaced etc. and .gitmodules
and .git/modules/
has been updated or blocks some things. Loooooong story.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With