Here is what I do in my projects - I have a similar software stack. Bitbucket (for Git and GitLab stand in), Jenkins, Force Migration Tool. Every Dev has their own sandbox and I use 3 release sandboxes (plus production) - Developer Integration -> User Acceptance Testing -> Stage -> Production.
There are two key things here:
- Git is the source of truth
- Master Git branch represents production - you should be able to deploy master into any sandbox and it would be similar to a refresh
With this in mind you should be following the standard GitHub workflow:
Using this your Pull Requests will contain the change artifacts that you need to deploy through your release environments. As Pull Requests are created/updated/approved Jenkins can verify/deploy using the force migration tool to the appropriate release sandbox.
If you have multiple projects happening at the same time it gets a little more complicated as the active pull requests may end up conflicting with each other such that UAT may reflect the latest deployment and not all the active open pull requests. For this we have a separate branch in Git that we merge all open pull requests into first (A UAT branch) that is the branch you want to deploy to Integration and UAT environments so you are not overwriting other changes from other pull requests.