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.1will bring in commitD. (Merging it intoV1.0will bring in bothCandD.)I'll just assume we're working on
V1.1; the same apply toV1.0except there will sometimes be more to do to handleCas well. (And I'm not sure why you changedV1.1tootherbranchin your edit but I'll go withotherbranchbelow because I did a lot of copy-pasting....)You have at least the following options:
--no-ffif 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.Das 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"
Dand then making one single commit produces the same graph (and files) as cherry-pickingE-through-Gwith--no-commitand then committing the result.A real merge gives you an actual merge commit:
If you allow
git mergeto make the commit (and it is able to do so on its own) the resulting tree will contain the changes made inDsince the merge-base forotherbranchandtopicbranchis 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 commitMwill omit the changes inD.The drawback is that on a future attempt to merge
masterortopicbranchintootherbranch, git will believe that the changes forDare in there (because they were, you just removed them).Making a separate revert commit for D gives this graph:
where
Ris the "revertD" change. Again, a future attempt to mergemasterortopicbranchintootherbranchwon't attempt to introduce the changes from commitDas git can tell that they were already put in. (The only difference between this and the previous scenario is that the fact that you tookDout 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
Monly 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 commitwill make a two-parent commit. So you get this graph:(where calling this commit
Mis kind of a lie since it's not a merge, but what the heck :-) ). Again you can choose whether to reverseDbefore making commitM, or after, with the same reasoning applying. In either case, though, sinceMis 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 introduceEthroughGagain 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 showthe commits will basically match the patches you see forgit showofE,F, andG). If you combine them into one big new patchN, thenNwill look very similar to the wayMlooks in the squash-merge case, except that you won't have to "un-apply"Das 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
Nthat introducesE+F+Gas it were, you can do onegit cherry-pickofN. Which is not actually any easier than one cherry-pick ofE F G, except maybe to think about.)