So, essentially branches are the way to go when trying out new ideas or isolating features or sections of work. One can branch out of the master line and work on a feature, independently from what’s going on the outside, little introvert f*ckers branches are…
Git handles context switching very fast, meaning that switching branches replaces the files in the working directory with those of the selected branch without too much hassle. Eventually, branches can be either merged into the master branch or discarded completely.
By itself, the command displays the branches of the current repository:
git branch # Show the local branches git branch -v # Show the remote branches git branch -a # Show all branches, including remotes
To create a branch out of thin air, use this command and give it a nice name (no, Branchie is not a nice name):
git branch <significant-branch-name>
Of course, if you f*ck up, you can always rename it using:
git branch -m <old-branch-name> <new-branch-name>
git branch -d <delete-me>
Note that if a branch was not fully merged, that command won’t do it, but you can force it to do it anyway and it won’t even mind:
git branch -D <delete-me>
And now it’s probably the time to bring back our old pal,
git checkout. Apart from replacing your local files, it can also switch branches, thus moving the pointer to the new currently active branch and replacing the contents of your working directory.
git checkout <branch-name>
It can also do super combos: create a new branch, make it reference another branch or tag and switch to it.
git checkout -b <new-branch> <parent-branch>
Another quick option that can come in real handy,
git branch --merged will display branches that were merged and basically contain each other’s commits, more precisely it displays a list of all the branches that contain all the commits that the currently active branch has.
Note however, that Git will not allow branch switching if the current branch’s working directory is not clean, in the sense that there are no files in conflict – there’s no problem with new files or changed files that have not been altered in other branches. In the event of possible conflicts, either stage and commit the changes, or remove them.
To merge a branch, first checkout the branch you want to merge things into, then run the
git merge command.
git checkout master git merge <branch-name>
That command will add the commits from <branch-name>, on top of those on the master branch and then move the HEAD pointer at the top of the list. But, based on the level of their complexity, merges can be of two types: fast-forward merges and real merges.
Fast-forward merges occur when the parent branch doesn’t contain additional changes from the time of the branching, meaning that it’s absolutely safe to just move the source branch’s commits at the end of the parent branch and then simply update the HEAD pointer with the last position in the chain.
Real merges occur when the parent tree contains additional commits that were submitted in the meanwhile, between the times of branching and merging. Git tries to auto-merge branches using a recursive strategy. But there can always be such things as merge conflicts.
Conflicts are usually handled automatically if the file differences are on different lines, but otherwise, put yourself in Git’s position. Without any knowledge of what any of those lines mean, how do you decide which version is more adequate or better yet come up with a hybrid solution that takes incorporates both changes?
When that happens, Git throws a conflict error and the merging operation is suspended until the conflicts are resolved. So, the files that are in conflict now contain both blocks of code, enclosed between lines of
Manually reviewing those changes and removing the delimiters is preferable to using automatic merge tools as it is considered safer. After the conflicts are fixed, we can add the files to the staging index and perform a simple commit without any message – that will be prompted by the merging operation itself in a freshly opened editor.
Now that we talked about the two types of merges, here are a couple of useful options:
The first one inhibits fast-forward merges and forces git to copy all the commits from the branch to the parent tree, thus maintaining a detailed history log, even if fast-forwarding would have been possible, in which case only one commit would have been created on the parent branch to indicate the fact that there has been a merging operation:
git merge --no-ff <branch-name>
The other one will perform a merge only if fast-forwarding is possible and abort the operation otherwise:
git merge --ff-only <branch-name>
To reduce the probability of having conflicts occur when merging branches, we can merge the parent branches into the working branches, and do it often – this way when the working branches will pretty much contain the same thing as the parent ones, thus making it easy for git to perform successful auto-merges or even fast-forward merges. This strategy is called tracking and is commonly used when working with remotes.
Note: It is advisable to only perform merges with a clean working directory. Otherwise, things could get messy and we would have to talk about using stashes. Oh, what the hell – let’s get it over with:
I know it sounds like a porn term, but in actuality, the stash is a special fourth area in Git. It is separate from the repository, the staging area or the working directory and doesn’t have a checksum associated with it.
Stashes are commonly used when trying to switch into a branch while the working directory is not clean. Typically you would have two options: discard your changes or commit them. The third option would be to stash those changes and deal with them at a later time.
git stash save "Message goes here"
What the command really does, is it stores the changes into the stash with the specified message, so that they can be accessed later, and then performs a hard reset to HEAD e.g.
git reset --hard HEAD, taking the pointer back to the previous commit.
Here’s another fun command, to display all the stashed entries, with their associated identifier and message.
git stash list
Just for future reference, a stash identifier looks something like this:
This next one displays a diff between the changes in the stash and those in the HEAD.
git stash show -p <stash-identifier>
And well, here’s the rest of them:
git stash apply <stash-id> # Apply <stash-id> git stash pop <stash-id> # Apply <stash-id> and remove it from stashes git stash drop <stash-id> # Remove <stash-id> from stashes git stash clear # Remove all stashes
That’s it for today. Marilyn Monroe once said “if you can make a woman laugh, you can make her do anything”, to which I must add that the same thing applies if you can make her branch. Next time we can already start talking about remotes, moving up in the world like that… Here you go: How to: Work with remotes in Git (Part VI).