I have some questions about git and good practices ...
The state of the git repository is:
V1.0 : B.A--B.B
V1.1 : / C.A--C.B
/ /
master: A--B--C--D
I have a master, and 2 versions : 1.0 and 1.1.
A new feature must be developed and will have to be applied on 2 branches : V1.1 and master.
What's the better way to do that ? I guess I have to create a feature branch, but based on which one? master or V1.1?
What will be the best merge strategy once the development is validated ? merge ? cherry-pick ? rebase?
The feature branch will be pushed to upstream, because I won't be the only one to work on it. There will also have more than one commit.
Thanks for your help !
If the feature branch is based on master, I'll have this :
V1.0 : B.A--B.B
otherbranch :/ C.A--C.B
/ /
master: A--B--C--D
\
topicbranch: E--F--G
Once the feature development is finished, I can easily merge master and topicbranch to add the new feature into master.
But how to add commit E, F and G into otherbranch (just after C.B)? That's where I think that
checkout otherbranch;
git merge topicbranch;
won't work, because it will also add the commit D.
You are correct: merging the new topic branch into
V1.1
will bring in commitD
. (Merging it intoV1.0
will bring in bothC
andD
.)I'll just assume we're working on
V1.1
; the same apply toV1.0
except there will sometimes be more to do to handleC
as well. (And I'm not sure why you changedV1.1
tootherbranch
in your edit but I'll go withotherbranch
below because I did a lot of copy-pasting....)You have at least the following options:
--no-ff
if needed, although it isn't), then revert it.--no-ff
) with--no-commit
, then reverse-applyD
(easiest withgit revert --no-commit
, but you can alsogit show D | git apply -R -
, for instance), and commit the result.D
as with either of the "real" merges.--no-commit
)E
,F
, andG
.These all result in somewhat different commit graphs, except that squash-merging and "un-applying"
D
and then making one single commit produces the same graph (and files) as cherry-pickingE
-through-G
with--no-commit
and then committing the result.A real merge gives you an actual merge commit:
If you allow
git merge
to make the commit (and it is able to do so on its own) the resulting tree will contain the changes made inD
since the merge-base forotherbranch
andtopicbranch
is commitC
. But if you suppress the commit, and then—in the work directory—back out the changes made in commitD
, and only then commit the resulting work-directory files, the tree for commitM
will omit the changes inD
.The drawback is that on a future attempt to merge
master
ortopicbranch
intootherbranch
, git will believe that the changes forD
are in there (because they were, you just removed them).Making a separate revert commit for D gives this graph:
where
R
is the "revertD
" change. Again, a future attempt to mergemaster
ortopicbranch
intootherbranch
won't attempt to introduce the changes from commitD
as git can tell that they were already put in. (The only difference between this and the previous scenario is that the fact that you tookD
out is explicitly recorded as a separate commit—not that anyone is likely to notice unless they go look, but it might be easier for them to find if/when they do go look, than if this is just mushed together into the merge commitM
.)In git, a "squash merge" commit is exactly the same as a regular merge commit except that
M
only has one parent. Git goes through the same merge machinery as usual, it just does not make the final commit, nor set things up so that your manualgit commit
will make a two-parent commit. So you get this graph:(where calling this commit
M
is kind of a lie since it's not a merge, but what the heck :-) ). Again you can choose whether to reverseD
before making commitM
, or after, with the same reasoning applying. In either case, though, sinceM
is not an actual merge commit, a later attempt to merge in the branch will try to introduceD
, since the merge-base has not moved. (It will also try to introduceE
throughG
again of course. If you get lucky, git will be able to tell that they're already in there, and the merge will just automatically "think" something like "oh, OK, already there, proceed!". If not, you'll have some manual cleaning-up to do.)Finally, cherry-picking commits makes copies of their changes, or—with
--no-commit
—just applies their changes and skips the commit part. So assuming you combine all three into a commitN
, you get this as the graph:If you leave them separate you get
E'--F'--G'
(where these names indicate the patches you'll see if yougit show
the commits will basically match the patches you see forgit show
ofE
,F
, andG
). If you combine them into one big new patchN
, thenN
will look very similar to the wayM
looks in the squash-merge case, except that you won't have to "un-apply"D
as you won't have brought those changes forward.Either way, you then have to repeat the whole process for branch
V1.0
.(But note: if you have one commit
N
that introducesE+F+G
as it were, you can do onegit cherry-pick
ofN
. Which is not actually any easier than one cherry-pick ofE F G
, except maybe to think about.)