Is it better (safer) to merge "master" into "final", then merge that back into "master"? Or will merging directly into "master" from "final" retain the commits there previously?
The latter: merging directly into "master" from "final" retain(s) the commits there previously.
git is different than other version control systems like SVN. For the unfamiliar, SVN treats branches like "buckets" - the branches/buckets are fixed (stable) and you had to be careful about moving commits between buckets. In SVN, you had to merge (copy) any recent 'master' commits into 'final' before you 'reintegrate' (pseudo-merge) the 'final' branch back into 'master'. 'Reintegration' effectively copies the tip of 'final' onto the tip of 'master', effectively replacing 'master' with an exact copy of the tip of 'final'. You could lose any commits in 'master' that weren't merged (copied) over to 'final' first.
Like I said, git is different. In git, I like to think of the repository tree (the commits) as stable and rather its the "branches" that move - think of branches as little labels/decorations that hang off of commits.
I'm going to draw your tree a little differently - in fact, I will draw it before commit 'i' was created:
final
|
e - g
/
a - b - c
\
d - f - h
|
master
Now let's commit 'i':
final
|
e - g - i
/
a - b - c
\
d - f - h
|
master
Only 2 things changed. (1) Ignoring the branch "decorations" for the moment, we see new commit 'i' was created from its parent commit 'g', and (2) the final "decoration" moved from commit 'g' to 'i'.
Let's merge final into master - that is, let's update master so that it includes the changes from final:
final
|
e - g - i
/ \
a - b - c j
\ /|
d - f - h |
|
master
Now the 'master' branch decoration moved. The 'final' branch decoration stayed put. The 'j' commit is a merge commit created from 2 parents: 'h' and 'i'.
But what if we had merged master into final instead - that is, updated final so that it includes the changes from master:
final
|
e - g - i |
/ \|
a - b - c j
\ /
d - f - h
|
master
Now 'final' moved and 'master' stayed in place. Note that the merge is a true merge - the changes from both branches are in commit 'j'. 'j' is the same no matter which branch merged into which - the only difference is which branch "decoration" got moved. (And those branch "decorations" are easily moved around.)
For completeness, let's merge the other branch back into the first branch (doesn't matter who merged into who first - as long as we merge in the other direction this time). Note that only the decoration moves - no new commit was necessary (in git jargon, it was 'fast-forwarded') because 'j' already contained both sets of changes from both branches:
final
|
e - g - i |
/ \|
a - b - c j
\ /|
d - f - h |
|
master
In fact, let's look at the tree a few days later (I deleted 'final' branch - I was done with it):
e - g - i
/ \
a - b - c j - k - l - m
\ / |
d - f - h |
|
master
Okay, can you tell from the tree which branch was originally 'master' vs which was originally 'final': e-g-i or d-f-h? Does it matter?
In git, merge are true merges. There are no "buckets" and you don't have to move commits between "buckets"/branches like you do for SVN. There's only the "tree" that you are evolving and the branches ("decorations") are bookmarking your place(s) in it.