Before
A1-----A2 (master, contains a:apple, b:banana, c:carrot)
\
\----B1 (second, contains a:apple, b:beets, d:dandelion)
- Current branch: second
git status
: clean
Variant 1: no reference to master (very ugly)
git 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)
- Current branch: second (ff)
git status
: clean
diff
status to master
: clean
Confirm 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
- as option
-u
is given, it updates worktree accordingly
- Then this is committed onto B1 as usual
- However it does not add a reference to the "merged" tree.
Variant 2: show a merge reference to master (preferred!)
Sorry, 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)
- Current branch: second (ff, merged, first parent: B1)
git status
: clean
diff
status to master
: clean
Confirm 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.
- However we want to use option
-u
, as this is crucial for updating the worktree. But -u
requires -m
or similar which throws us out of merge mode.
- So we do a dummy merge before (and use strategy
ours
to prevent merge conflicts, which is exactly the wrong thing we want to happen).
- Then we read in the state from master using
git read-tree
as we wanted
- And now we
amend
the merge with the now, finally correct, index contents.
- This keeps the original merge information.
- Note that this is
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.
- However this introduces a nonatomic situation, where a race condition (power outage) can put your local git repo in some unwanted state
- This is bad in case this strategy shall be used in an automated setup, where, for example, after a release (B1) the current upstream (master) shall be used to restart to create the next release (B2 and later).
- So variant 2 cannot be seen as the definitive last word to this. Yes, it works, somehow, but it is neiter nice, clean nor perfect.
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).
TODO
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.