Like you, I felt much more comfortable using git after learning this.
Yeah, completely unreachable commits have 30 days, even when you run git gc. The default reflog for a branch is even longer: 90 days!
I often try to reassure people new to git that "if you'll just commit often, there's basically no way you can lose work so that I can't help you get it back. Apart from deleting the whole repo folder. Don't delete the repo folder.".
I also have the habit of preventing non-fast-forward pushes on origin/master, which also helps when I can tell my team that they can't trash the origin even if they try.
# Merge commit
git merge --no-ff -m <message> <hash>
# Squash and merge
git merge --no-commit --squash <hash>
git commit -m <message>
# Rebase and merge
git rebase --force-rebase <hash>
From https://stackoverflow.com/a/52301456I believe in clean, linear history, and strongly prefer rebase-based workflows to merges. That's actually one of the reasons I chose Phabricator for my current place, as it is also very opinionated towards the same way of working.
Edit: oh, and to answer your actual question, the third one.
Years ago when I was just reading about git instead of using it, I saw sentiments along the lines of "always use feature branches and merge them so your thoughts and process can still be looked at later". In the last ~5 years or so I've worked professionally, I've not once wished I could reference intermediate commits in my own code or someone else's. I've found that ambiguities and clarifications can and are caught during the code review process.
I'd say another couple of benefits:
- It's relatively easy to teach git workflows when the log/graph is linear, and similarly it's _way_ easier to reason about your workspace when you have an actual production codebase.
- Merge commits can make certain operations like reverts and patches harder to reason about
One command I often use is git blame which allows me to find the commit that's associated with a particular line of code. Then I can look at the commit message and the diff against its parent. Perhaps what I'm changing may undo a bugfix and I wouldn't have realized it without reading the associated commit message.
When I look back at the history, I'm only interested in seeing the changes that actually made it through, not every intermediate alley and dead end in between. If those dead ends are significant discoveries/results, I document them elsewhere.
But either way works fine. It just gives you a different history. My team likes merging because they don't understand exactly what happens when rebasing. In that environment `git log --topo-order` is practically a necessity, though.
If you are merging master into a feature branch that has ongoing work and continues without merging back to master, that's the problem.
If your feature branch is short-lived, it can be easily rebased.
If your other branch is more like a release branch, with lots of work that can't be rebased easily, some times you can't really avoid a merge from master without communicating it first. If your team is large or distributed it might not be practical to say "release has moved to (rebased ref), please catch up"
In that case you should treat merges to release the same as merges to master (they should be finished bits of work that are considered published) and any unmerged features for the release, are kept on feature branches that are based on the release. They can be rebased after the point where master is merged back into release to avoid the nasty merge conflicts.
git merge —-squash <tree-ish>
When merging into the baseline git merge —-no-ff <tree-ish>
The reason for the latter is subtle. Yes, a perfectly linear history is nice in the aesthetic sense. However, the merge commits are artifacts of reviews that are useful in process audits, which QA and QC like to preserve.