Thanks for the addition. Yes, I didn't mention .forceignore. My additional two cents on that subject:
For me, the problems with .forceignore are:
1. It is somewhat poorly documented (though has been improved recently). Knowing exactly what to put in the file can be challenging; the syntax for identifying specific metadata components is simply the file path for push/deploy, whilst is just the API name and metadata type name for pull/retrieve. The
documentation does specifically cover a profile example, but identifying the correct metadata type name doesn't always appear to match the folder structure or *-meta.xml file extension. Please correct me if I'm wrong.
2. As the documentation now says (it didn't used to), this is applied not just to force:source:push/pull/status but also force:source:deploy/retrieve.
What's the "so what" for point 2?
Whilst IC2 appears to continue to use the Tooling/Metadata API to deploy metadata, we have scripting that uses sfdx to specifically push and deploy our metadata in stages to newly created scratch orgs since we have a need to deploy certain metadata just the once during the creation of a scratch org, for development purposes (long story short we need to explicitly enable Shield on certain key fields on certain objects to validate that our AppExchange package is Shield compatible and that means jumping hoops when first deploying our package's metadata to a scratch org). Thus we are unable to use .forceignore to exclude our packaged permission sets and other materials since it would prevent the metadata from being sent to the scratch org in the first place.
Anything else?
The force:source:pull command is rather blunt force (pardon the pun). You cannot be selective; you'll get all metadata that has changed or been added (excluding those listed in .forceignore, of course). It's common to play on scratch orgs during development and many such changes are destined for the trash, yet force:source:pull can't be told to ignore these without manual changes to .forceignore (which you probably don't want to touch because that's often version controlled too, plus knowing just what to use for the name can be challenging as I already said).
This can easily lead to accidental and inappropriate updates of metadata in your local source tree and, without due diligence, may lead to accidental inclusion in commits to git (or other VCS). This then means wasted time reversing out such changes, which can be non-trivial depending just how late the inclusion is spotted.
More still?!
Related to Dave's comment about retrieving only changes for profiles/perm sets, I've found that retrieving profiles (and, I assume, permission sets), will only fetch data that relates to objects that you retrieve at the same time. This is where the Retrieve for Merge is really helpful because you can select all objects on the org (and/or selectively exclude or include the ones you want) and get the profile/perm set fully populated, whilst having no impact on your local metadata for these objects.