You will find a dizzying variety of workflows out there, but at some point they pretty much all boil down to pulling in your team’s changes, merging them with your own, and delivering them back to the team.
Here’s one way to accomplish it: Start and work on a local master
branch that tracks the remote origin/master
, where your team will deliver integrated changes.
git checkout master
- The branch from which you will push changes.git pull origin master
- Catch your local master
up with latest changes from your team.master
.git pull origin master
(in case more changes have been pushed by teammates while you were working).git push origin master
- Share your scintillating creativity with your team by “catching-up the remote origin/master
to your master
.This workflow is very common for me! Another variation of this workflow is “started-working-on-wrong-branch”. Both have the same basic solution: Get to the right place, and then cherry-pick the change(s).
It typically goes like this:
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:walquis/learning-git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Let’s re-enact this scenario in our own forked repos. Let’s start by moving our local master branch to one commit behind origin/master. (How would we do that?)
Now change a file (e.g., add a line to git-aliases.sh), and add/commit/push.
What happened? What now?
A couple of options:
$ git reset --hard HEAD^
$ git pull
$ git cherry-pick <sha-of-commit-you-couldn't-push>
Let’s make a merge collision happen: Branch, change the same line of a file, commit and try to merge.
$ git show # How about the .gitignore change?
$ git config --global core.editor "code --wait" # Set your editor to vscode
$ git merge master
Auto-merging .gitignore
CONFLICT (content): Merge conflict in .gitignore
Automatic merge failed; fix conflicts and then commit the result.
$ git status
$ code .gitignore
Git has modified our source file!
Note that vscode has added some helpful “phantom text”--‘(Current Change)’ and ‘(Incoming Change)’--to help clarify what’s what. Note also, that text isn’t part of the file.
When you’ve decided what the file should look like, ‘git add’ to tell git it’s OK now (as git itself tells you when you do ‘git status’).
Then finish the merge with a ‘git commit’ (again, as ‘git status’ instructs)..
$ git stash # Push current work--your index and modified stuff--onto a "stash stack".
$ git status # Now your workspace is clean (except for files that aren't in git).
$ git checkout -b someNewBranch # Or some existing branch
[Do the work that has pre-empted you, commit/push/etc, and now return to your story]...
$ git checkout master
$ git stash pop # Restore your current work to this workspace
$ git stash list # See if anything still on the stash stack.
This is another instance of using git to practice good communication skills. Nobody wants to see my stumbling around; they just want to see the final draft.
Assuming I’ve made a series of commits I don’t want people to see, I can turn them all into one commit, while still keeping the same content!
(You may first want to set your git editor to vscode, as described above in “Resolve Merge Collision”).
$ git config --global core.editor "code --wait" # Set your editor to vscode
$ git rebase --interactive <some-commit-before-the-commits-to-fix>
See the git rebase tutorial for details on how to work thru the rebase.
Sometimes a bug creeps in and it’s hard to find where it happened.
$ git help bisect # You can also 'git help' any other git command.
$ git bisect start <known-bad-commit> <older-known-good-commit>
[Test the code in your workspace to see if the current version is good.]
$ git bisect good # Or bad, if the test fails
[Git will keep iterating until the bad commit is identified.]
...
$ git bisect reset # Go back to the head of the branch
This is effective, but a little tedious. What if we could automate it? If you can create a test.sh script that git can run for you, which will exit with status 0 (true) if the test succeeds, and non-zero (e.g. 1, which will mean false), then git bisect can use that script:
$ bash test.sh
$ echo $? # 0 if good, 1 if bad
$ git bisect start <known-bad-commit> <older-known-good-commit>
$ git bisect run bash test.sh