Now you're all set to commit changes, just like if you manually modified and staged (with
git add) the files yourself. Now type
git commit -m "Merged remote version of readme with local version." and then
Figure 12: Status after the new commit.
Before we go on, if you noticed, there's a lingering "README.md.orig" file. That is just a backup file. However, it's a pain to deal with these "orig" files. For this time, you can move the file somewhere else, or just delete it, but for future reference, check out this page on the many strategies you can leverage to deal with those files.
Back to the merge. Look! Your branch is "ahead" of "origin/master" by 2 commits. Let's see what those commits are. To show just the last two commits, type
git log -n 2.
Now, let's push our changes to origin/master and see what happens. Type
git push origin master. Now, just to be sure, we're not going to look at the "local version" of the remote branch. Let's go right to Github to see what happened. View the commits in your repository:
Figure 13: GitHub page showing commits.
What might not make sense here, is that you have first the GitHub-side readme commit, then your local readme commit, then the merge. It doesn't make sense for all of these commits to happen in sequence, since the first two are conflicting. What happens is that your local readme file commit is logged as a commit on a separate branch that is merged in. Let's graphically demonstrate that by clicking on the "Network" button on the right (circled in red in Figure 13).
Figure 14: GitHub's timeline on commits and merges.
Each dot in this diagram represents a commit. Later commits are on the right. The one that looks like it was committed to a separate branch (your local master branch) and then merged in is the commit of your local version of the readme file. Hover over this dot and see for yourself.
Before heading into discussions of workflows, I want to touch on a feature that Git does uniquely well, and that's worth knowing about should the need ever arise. It's called "rebasing." With it, you can shape your commits the way you prefer before merging them to another branch. You can already do some preparation when you're staging your files. You can stage and unstage files repeatedly, getting a commit exactly how you want. But there are two main things that rebasing lets you do in addition to that.
Let's say you were working on branch
A and you created branch B. Branch
B is nothing more than a series of changes made to a specific version of branch
A, starting with a specific commit in branch
A. Let's say you were able to take those changes and reapply them to the last commit in branch
A! It's as though you checked out branch
A and you made the same changes. You can use rebasing to allow your merges to be "fast-forward," so when you merge subsequent changes into another branch, there's no "merge commit." Your changes are simply added as the next commits in the target branch, and the new latest commit of that branch is your last change. This is a powerful feature.
One of the most common Git workflows is the pull request, which shows up a lot in open source projects. Commits are often grouped into "feature branches," representing all the changes needed for a branch. Projects with designated maintainer(s) often operate is as follows:
- You initially push your "feature branch" to a remote repository. This is often your fork of the main repository.
- You create a "pull request" on Github for that branch, which tells the project maintainer that you want your branch merged into the master branch.
- If the branch is recent enough or it can be rebased onto master without any conflicts, the maintainer can easily merge in your changes.
- If there are conflicts, then it's generally up to the maintainer to do the merge or to reject the pull request and let you rebase and "de-conflict" the commits in your branch yourself.
At New York Magazine, where I work, we generally have four main branches of each project entitled
devbranch: While developers first test their code on their own computers, eventually they need to test changes on a server with shared resources. This exposes a bunch of integration issues and often requires multiple commits (multiple attempts to get it right) before the change is complete.
qabranch: This is branch is for QA (quality assurance) testing to be done on a new change. The branch is cleaner, consisting only of completed changes. While everything isn't necessarily optimized (maybe you do have debugging information being recorded to the log, for instance), it's much more controlled as opposed to
stgbranch: Changes approved by QA go to the "staging" environment. This environment is fully optimized, as if it were the production environment. There could be more issues that are exposed by testing in a fully optimized environment, but usually not. This is not to be confused with the much lower-level staging in Git, but ultimately, the concept is the same. You're ultimately preparing a set of features that are slated to go public, rather than a bunch of file changes that are about to be committed.
prodbranch: What your clients/customers/users ultimately see is deployed directly from this branch.
We rely on the open-source continuous integration server Jenkins to monitor each branch. When any change is made, the project is built and redeployed to a computer/server dedicated to that environment. To manage the environment-specific configuration, including enabling optimizations and altering logging levels, we use Puppet. We also use Git to maintain our internal documentation, written as text files using the Git-variety of Markdown, to allow ease of collaboration and code-friendly formatting.
Each commit message at the magazine, optimally, should have a story number. A "story" is a description of a desired modification. If something should be changed in code, someone describes how the change works in a web interface provided by a story-tracking application such as Atlassian's JIRA, which we use. A developer can modify the "status" of the story to reflect progress being made toward its resolution.
We use Atlassian Crucible for peer code reviews. This lets a developer send a series of commits out to fellow developers to have a look at. It tracks who has made a change to review your code, and gives them the opportunity to make comments.