As Keith C stated in his excellent answer, there are some good sources for detail on use of SFDX, leveraging scratch orgs.
My tuppence extra is that selecting git as your VCS is a great choice; many IDEs support git (including VSCode and IntelliJ IDEA, both of which can be used for Salesforce development) and you'll find many Salesforce-related resources in public git repositories.
Which git implementation you select comes down to personal/corporate choice. However, in order to support CI (e.g. on attempt to merge a PR to your master or patch branches) you need one that allows you to execute scripted processing.
We use Bitbucket with "pipelines" - we have a Docker image for CLI use of sfdx during pipeline execution. The majority of the time spent executing the pipeline is actually spent waiting for Salesforce to create a scratch org, then waiting for sfdx to finish pushing all the metadata and finally waiting for sfdx to complete executing the unit tests.
Whilst Bitbucket comes with free "CPU minutes" for pipeline execution, you'll find these are used up really quickly - though YMMV - and you'll have to pay for the minutes the CI executes for (even though all the real CPU usage is on the Salesforce side, not in Bitbucket).
In terms of a branching strategy, since we develop and maintain a managed package we have patch branches as well as the master branch.
We use a branch per bug, task or user story. When the code is ready we use a PR (pull request) to get it reviewed, and then (on success) the PR is merged, triggering the CI.
NB: When necessary we back-port a bug fix to a patch branch by creating a bug-specific branch against the patch branch, cherry pick the fix commit back, create a PR and merge it. We have blocked direct commits to master and the patch branches which is why we always use a PR.
Every time we have a new branch (or when CI is executed) we also create a new scratch org. It is to the scratch org that we push all the metadata.
Developers always use the scratch orgs for any development against the bug fix, task or user story they are working on, and only that developer accesses this scratch org (except in a new specific cases). All our unit tests generate all the test data they need and we have some Apex for generating data to give something for the developer to play with.
Our CI is all about ensuring that the latest versions of the code, in master and in patch branches, have no test failures and have enough test coverage. We don't ever push our metadata directly to any orgs other than scratch orgs. The creation of the package is handled separately (though still based on the content of git). As such, developers never push anything to any sandboxes or production environments. This is something only done once we have an official package release available, and is done by the implementers rather than developers.