GIT Branch Create, Delete and Rename (Best Tutorial 2019)

GIT Branch Create GIT Branch Delete and Rename

GIT Branch create, delete and Rename: The Complete GIT Tutorial 

What they came up with is a version control system. The basic principle of version control is this: instead of keeping only the latest copy of something, you hold on to each successive revision as you work so that you can refer or revert back to an older version if you need to.


Although you can use software tools—one of which is the subject of this blog—to help you, version control is, more importantly, a practice. It’s something we do—not just the tools we use to do it, At the last, I will describe how to create git branch, and how to delete and Rename git branch.


Many of us have had projects where we kept copies of old versions of our work, saving new versions by using an app’s Save As... command to give each new copy its own name.


Perhaps we marked the new filename with the current date (project_2018-04-15.doc), or maybe we added a version number (mockup-1a.psd). Both are rudimentary, but entirely valid, forms of version control.


Version control systems like Git work by keeping a copy of each successive version of your project in something called a repository, into which you commit versions of your work that represent logical pauses, like save points in a video game.


Every commit includes helpful metadata like the name and email address of the person who made it, so you can pinpoint whom to praise (or blame) for a particular change.


These commits are organized into branches, each representing an evolutionary track in your project’s history, with one branch—the trunk, or master branch—representing the official, primary version.


Having built up a history of past commits, it’s easy to retrieve any previously committed version of your project, roll back changes, or compare two or more versions to aid in debugging.


In order to save changes to your repository, there needs to be one version of the project that you can safely make changes to. Version control systems like Git usually call this the working copy.


Its job is to act as a scratch pad for any changes you may want to make to the project; you’ll eventually commit these changes to the repository as an official, saved version.


From our perspective, a working copy is usually easy to spot—it’s the copy of the project that appears on our hard drives, as regular files.

Version control systems

Version control can seem laborious because, in a regular desktop workflow, we’re expected to save changes to our work twice: once to the working copy, then also to the repository.


As a young web developer starting out, I found this annoying enough to avoid version control altogether. Eventually, though, I came to appreciate the benefits of having every significant version of my projects stored, annotated, and neatly organized in a secure location.


It also helped me to think of commits as significant changes, as opposed to the hundreds of little changes I might save in a given hour.


The extra steps involved in committing—the brief pause from coding, having to write a descriptive message, occasionally having to stop and address conflicts between my version and someone else’s—have ultimately helped me develop a more thoughtful and judicious way of working.


Although adopting a basic version control practice is a little extra work, it’s not hard work. But like anything we do to stay organized, version control works best when it’s practiced consistently.


On one hand, once you’ve committed a version of your work to a repository, you should be able to trust that when you look for that version later, you’ll be able to find it in exactly the same state as when you committed it.


But you should also be able to trust that the version you’re looking for was committed to begin with, which means committing yourself to commit your changes at regular intervals as you work.




Versions of single files, like Photoshop documents, are easy to manage: each represents a complete copy of the project as it existed at a certain point in its history, and (if you’re using numbers or dates to identify versions) it’s easy to tell when that point was by just scanning the list of versioned file names.


But while some things we work on are neatly encapsulated in single files, others —like websites and apps—consist of entire directories of source files. How can we apply version control to projects like that? 


This is where software version control systems like Git really earn their place in our toolbox—in managing more complex projects like websites or the source code for an app, or when coordinating changes from lots of collaborators.


The simplest version control method is the same one we’d use for a Photoshop file: make a copy of the whole project directory, appending dates or incremental version numbers as we go along. The directory named our-website/versions/v12 is the twelfth revision to our project.


Just as with a single file, every time we make a significant change to the website, we’ll create a new numbered copy of the whole project: the one after v12 is v13, followed by v14, and so on.


Here, the versions directory acts as our repository, and each numbered directory is a committed version. In simple cases, this works just fine. Indeed, before I got into using version control systems, that’s how I managed web projects for clients.


Because a website consists of many different kinds of files, we need our working copy to be saved to the hard drive as its own directory: our website/working-copy.


The process for committing changes to this project is a bit more complicated than for our hypothetical Photoshop file, but only a bit: we make and test changes to the website in the working-copy directory;


Then, when we’re ready to publish, we commit those changes by making a copy whose name includes the next version number in sequence, such as versions/v13.


But what happens when we try to share this version control system with other people?


These days, sharing files—such as our store of committed versions—is the easy part. Your repository can be a shared folder on a service like Dropbox or Google Drive, or a networked hard drive or file server in your office.


Inviting a new collaborator to your project can be as simple as granting them access to that folder. Everyone who has access to the shared folder can potentially commit changes.


Where things get tricky is not simply in syncing changes between teammates, but in coordinating those changes so that your version control system remains trustworthy and viable.


This system of numbered, versioned directories and working copies may seem straightforward, but you can never assume that two people will interpret or follow simple rules in the same way.


To sustain a trustworthy system, it’s essential that the rules always be followed in exactly the same way.


If you work on a small team and are thinking, “Come on, it’s not that scary,” imagine having to explain, let alone implement, a system like this within a large corporation, or an open-source project like Linux with thousands of contributors.


This isn’t just a random example: Git was invented by Linus Torvalds precisely to meet the demanding needs of the Linux project.


For the sake of argument, though, let’s suppose that everyone on your team understands the rules and that any member of the team can reliably commit a new version. Here’s where things get really hairy: what happens when two people want to commit new versions at the same time?


The difficulty here doesn’t involve following the rules so much as communication. Once we reach the point where two collaborators might need to work with the same files at the same time, we run the risk of one person’s changes overwriting—or, to use my preferred technical term, “clobbering”—another’s person’s work.


Imagine you and I are collaborating on a website. You’re exploring what it would look like if we changed all the links on the site from blue to green, while I am considering changing the link color to red.


Under the rules of our version control system, I should make and save my changes to the copy of the stylesheet file in the directory.


Unfortunately, the rules also say you should make your change in the exact same place. Following this method, whether the links are going to be green or red in the next numbered version of the project depends entirely on which of us made our change last. If I saved my file later than you saved your file, my changes win.


The only way to avoid this clobbering is for individuals interested in submitting changes around the same time—you and me, in this case—to work together to make sure that either our changes don’t conflict or that our version control system somehow automatically reconciles or rejects conflicting changes.


Some primitive version control systems solve this problem by requiring people to “check out” a file, like a blog from the library. Checking a file out marks it as uneditable by anyone else until the person who checked it out has both finished editing and explicitly checked it back in.


This addresses the risk of unintended file-clobbering (by making accidental overwrites impossible), but it also creates a new problem: if I’m the one with the homepage file checked out, you’re stuck waiting on me to finish before you can do your work.




Instead of having one working copy shared by everyone, we can require all team members to have their own working copies stored on their own computers. In theory, at least, that allows each of us to work independently until it’s time to save a new official version.


There’s still a small risk of two people trying to commit versions at the same time, but that happens less frequently than just saving changes while working, and we can coordinate those kinds of changes easily—“Hey, I’m going to push version 34 of the website, everyone cool with that?”—via email or a tool like Slack.


With this system, we haven’t done anything to try to merge together different versions, with different changes, into a cohesive whole. Rather, we’re just making named copies of folders and assuming that our working copy is a trustworthy, canonical source for the next version.


The next big problem to solve is what happens when that stops being true, and our working copies drift out of sync.


In the diagram in FIG, your version of the site now has green hyperlinks, while my copy has red links, and the latest official version (v12) of the website has blue links. Both working copies are newer than the shared master copy, but beyond that, how can we know what color the links should be in the next official version?


More importantly, how can we know what else might have changed? What if, in addition to the changed link color, your copy of the website includes a lovely new page explaining the company’s mission that isn’t in my copy, and mine has a fix for an annoying JavaScript bug that isn’t in your copy?


This is where our homegrown version control system completely breaks down. It’s not that we can’t work together to combine our two working copies into a single version; it’s that doing so takes up valuable time we’d much rather spend writing, designing, coding, making coffee, or browsing Tumblr for cat GIFs.


Auditing our work files for conflicting changes is no fun; more importantly, it doesn’t scale. As we add more files, more collaborators, and more changes, the risk of accidentally introducing major problems increases.


Remember that version control is more than just the versions: it’s the rules and processes for managing versions. Once you have a lot of files, a lot of collaborators, or both, it can be exhausting to enforce those rules without a referee—and only by enforcing the rules consistently can you trust your version control system enough to get any value out of it.


Fortunately, all of these things—enforcing rules, keeping track of versions in a repository of past work, shuttling changes back and forth between the repository and your working copy, even merging together two directories and policing conflicts—are things that computers can do a lot faster and better than we can.


By learning and adopting an automated version control system like Git, we can keep our work neatly organized and our changes safely coordinated with one another, all without a lot of effort—that is, once we adapt and learn how to use such a system.




Git does for version control what web standards have done for our code or, for that matter, what Microsoft Word has done for word-processing documents.


Because Git is so ubiquitous, once you know it, you can send code almost anywhere. Git excels at synchronizing changes between different computers, whether servers like GitHub or your colleagues’ laptops.


Far-flung members of your team can use Git to combine efforts on a project, pushing changes to a central hub where collaborators can pull down their own copies or review work in progress, and then use Git to push changes to a web server for deployment.


Git’s way of staging changes and managing branches gives you unparalleled control and flexibility over how changes to your projects are committed and organized. These attributes make Git perfect for projects like the Linux operating system kernel, with thousands of contributors and hundreds of thousands of commits.


But Git also scales down beautifully for smaller projects and teams. Whether you’re looking to add version control to your personal site or share code with your whole company, learning Git gives you a seat at a very awesome table.


Note, though, that Git isn’t the only tool out there for automating version control or syncing files between collaborators. People sometimes rely on simpler file-syncing services, such as Dropbox, which offer shared folders and the ability to view and restore old versions of a given file.


If most of your work involves single files—Word documents, spreadsheets, PSDs—and something like a shared Dropbox folder is working for you, you may not need Git.




Git keeps your project in a local repository (usually a hidden folder on your hard drive). This is an important distinction between Git and older version-control systems like Subversion or the Dropbox-based versioning scenario mentioned earlier.


These server-based processes are centralized, in the sense that the only place you can get at your whole history of prior versions is a shared, remote space, and only your working copy is accessible offline.


Git is a decentralized version control system. Both your working copy and a complete copy of the entire history of the project reside on your machine, the server, and every other computer that hosts a copy of the project. By default, Git’s hidden repository folders live inside a visible working copy folder.


If you browse that directory, you’ll see only the files and folders you expect to see in a working copy of your project. This working folder is where you’ll make your changes.


Whenever you’re ready, you can easily move changes into the safe, stable repository by making a commit. In our semi-manual process, we “committed” a new version by making a copy of our working copy, naming it with the next sequential version number. Committing changes in Git is, conceptually at least, very similar.


For each commit, Git records the precise state of our files as they are right now in the repository for later access and retrieval.


Unlike in our manual example, where we had the annoying (and potentially risky) responsibility of making sure new versions were copied into place correctly without clobbering anyone else’s efforts, Git automates all of that busywork.


Even better, Git copies new versions incrementally, making references to existing copies of files that haven’t changed to conserve disk space.


Git not only takes care of safely copying data back and forth between the working copy and the repository (and between the local and server repositories), but it also provides a robust system for referring to different versions of the project.


 One of the small costs of establishing a manual version control practice is needing to decide, and then communicate to your teammates, the correct way to identify single versions. Should you use a number (like v12), or a date stamp (2018-07-28), or something else?


Git allows you to assign your own names or numbers to versions if you need to, but it also gives you a reliable, unique identifier for every single commit. If you don’t need to assign custom names or numbers to versions, you can just sit back and rely on Git to do that.


 Finally, Git also offers powerful tools for safely merging changes between different versions of a project—not just between different collaborators, but also between multiple variations of the project on one person’s computer.




Version control can be challenging for newcomers not (just) because it makes things complicated, but because change is legitimately complex. Using a tool like Git forces you to question your own assumptions about how change works.


For example, one of the things version control demands of us is a nuanced understanding of the state. As humans working in a virtual space, we’re used to applying physical metaphors as handy cognitive shortcuts for understanding digital things. Let’s go back to our scenario of changing the link colors on a web page.


Before our minds were trained in the philosophy of versions, we thought we had a file (like it was a physical object that just happened to be on the wrong side of a computer screen), and we were changing it. The CSS file remained constant, but the link color changed.


In fact, from the computer’s (and Git’s) perspective, there are at least three files: the saved copy from before we made the change (with blue links), the working copy where we replaced the line that controls link color, and then (finally) the new saved copy that replaces the old one.


But nothing about the mechanics of how this one line is updated changes the fact that styles.css appears to be one file to us. Semantically, viewing the pile of bits named styles.css as a single thing that changes is very valuable, because it helps us understand where to find our data.


Having to spend too much time concerning ourselves with the difference between the versions of this file is annoying: it’s better if we can rely on the name to tell us what file this is, and have some other way of understanding how it has evolved.


It’s more accurate to say that, rather than three different files, we’re talking about the same file in three different states. It’s the same file because even though its contents may change, its name stays the same; logically, therefore, it’s the same file.


One potentially confusing difference between our numbered file/directory names example and a true version control system like Git is that there is no giant folder full of old versions to look at.


As Git users, we’re expected to know that behind each logical copy of a file in our working tree, Git is safely storing all the old versions of the file, in each of its previous states.


We can comfortably understand a system where two files or directories are copies of each other, where one is a little newer or more evolved than the previous one because there’s an obvious real-world equivalent: manuscripts have second drafts, blogs have second printings, and so on.


The rudimentary version control method I described earlier was relatively easy to understand because we were simply moving files around on a computer, something we've all done many times before.


This new model feels less like writing drafts and more like time travel. It kind of is like time travel—and as anyone who has seen Back to the Future Part II can attest, time travel is a complicated business.


For files saved on our hard disks, our apps and operating systems do a fantastic job of hiding—abstracting—all of that complexity from us.


Instead of a flurry of versions moving back and forth between hard disk and memory, we just see an icon. Sometimes its contents change, and its “last saved” time is incremented accordingly, but visually and semantically it acts like the same file the whole time.


Not only does Git do a poor job of hiding that kind of complexity, it barely even tries. Git suffers from what I like to call an excess of simplicity.


Unlike many of the tools we use every day, Git doesn’t do much to map the things it does to familiar metaphors or symbols, the way OS X maps deleting a file to the act of dragging it into the trash.


Git’s design assumes that you not only know how version control systems work but specifically how Git works. You’re meant to interact with Git on its own terms.


Git has more than seventy-five command-line functions, every single one of which has a specific job, with specific inputs and expected results. There is no single “Save new version” command in Git.


Instead, to make a new commit—which is sort of, but not exactly, the same thing as saving a new version—you need to perform two or three different actions. Each of those actions has a legitimate purpose in Git-space, but none of them maps to something you’d logically do to a file or document in the real world.


But Git is also one of the most matter-of-fact programs around. It never does more than you tell it to do (though it can be easy to accidentally tell Git to do more than you wanted it to do).


On one hand, this means that we might need to speak to it in more laborious, stilted terms than we’re used to, which is itself an almost radical notion in an age when software can recommend a movie or summon you a taxi.


On the other hand, the fact that the scope of a given command is limited means that there’s also a limit to how much damage you can do at any one time.


If you do get into Git's version of trouble —like if Git can’t easily reconcile conflicting changes, or if it’s uncertain about where to commit your work—there is always a command that will get you out of it, often with whatever work you were trying to save still intact.


As we’ve seen, a good argument can be made for even small teams to use version control. But the internet has made it easier than ever for people on opposite sides of the globe to work together on all kinds of projects.


The open source movement has taken that even further by creating opportunities for thousands of strangers to contribute changes to projects seen and used by millions—collaboration on a wildly unprecedented scale.


When you’re trying to accept contributions from a community of thousands, version control becomes an absolute necessity.


So although version control may have started out as a form of insurance against mistakes, tools like Git have helped transform it into something much more compelling.


As both a practice and a set of tools, version control offers us a common framework for collaborating on and sharing all kinds of work with anyone, anywhere—not to mention a new way of understanding and managing change.


Thinking and working in versions not only helps us understand how projects evolve over time, but also gives us more say in how that evolution happens.

Now, how can we begin to incorporate thinking in versions into our workflow?


IF YOU’RE JUST STARTING TO LEARN GIT, I recommend sticking to the command line, at least at first. Git’s command-line interface is its native tongue. Typing commands and seeing the responses Git gives back is a great way to learn about how Git actually works, which will pay off when you inevitably run into a confusing situation down the road.


The command line is also consistent across the various platforms Git runs on. If you know how to interact with Git via commands, you’ll be able to use Git no matter what kind of computer you’re on.


The command-line interface gives you the fullest access to all of Git’s powers, but there are also graphical Git apps out there, some of which are very good. And although I recommend starting out with the Terminal, you don’t have to choose one over the other.


You can in fact use the Terminal and a Git app side by side, making commits and pushing changes in the app, where it may be easier (or more your style), and relying on the command line for everything else. For now, let’s start with something relatively easy: getting Git on to your computer.




Starting with version 10.9 (Mavericks), released in 2013, OS X automatically downloads and installs command-line tools like Git the first time you try to use them.


If you simply open up the Terminal app and type a Git command, your Mac will prompt you to install a package called Apple Command Line Tools, including Git and several other utilities. After installation, OS X will automatically download and install updates to Git via the Mac App Store app.



The Git development team maintains an easy-to-use installer package for Windows that you can download from the official Git website (


The install wizard will ask you a bunch of questions about how you want to configure and use Git; if you’re unsure how to answer any of them, just go with the default settings.


If you’re comfortable with either of Windows’ two standard command-line environments, Command Prompt or PowerShell, the Git installer will give you the option of configuring Git to work with those.


By default, however, the Git installer provides its own terminal application, called Git Bash, which emulates (that is, works similarly to) a Unix-based system such as OS X or


Linux, with support for not just Git but all of the commands we’ll be using in this blog and throughout the blog. If you’re accustomed to using Git on one of those platforms, or if you want the most consistent command-line experience across different computers, Git Bash is an awesome tool.


Although Windows and Linux/OS X both support the same encodings for plain text files (ASCII and Unicode), they use different line endings, that is, different characters for denoting line breaks.


Many Windows or cross-platform text editors, including Atom and Sublime Text, already do a good job of handling line endings correctly, but Git may not always know which format it should use.


Git for Windows helps us out here by offering to automatically convert Windows line endings to Unix ones when you commit, and vice versa when you check out.

Even if all of your teammates and servers run Windows today, it’s usually best and most future-proof to commit Unix line endings to ensure that your files can be checked out safely on non-Windows systems.


One more small difference: while Windows paths use backslashes and drive letters, as in C:\Users\David\myproject, Unix paths use forward slashes.


An equivalent path on my Mac would be /Users/David/project (with no leading drive letter, since Unix systems like OS X don’t use them).


Here, too, Git Bash helps us out: it automatically swaps slashes, presenting that Windows path as /c/Users/David/project. (The leading /c/ indicates the C:/ drive, following Unix convention of referring to mounted disks as directories at the topmost, or root, level of your computer’s file system.)




If this is your first time interacting with a command line, entering weird bits of jargon after a prompt may seem scary unless you’re a programmer, sysadmin, or computer scientist.


It’s an understandable fear: the command line is a little scary. Your computer will let you do things from the command line that it would rightly stop you from doing from the Finder or Control Panel—like delete your operating system, not to mention your entire hard drive.


Keep in mind, however, that although the command line is powerful, on another level it’s also a little stupid: it will only do what you tell it to do, and most commands are set up to perform a single, simple task.


Before we dive into the specifics of using Git, I’ll go over how command-line interfaces work, and how to read the examples that appear throughout the blog.


What the command line is for

The command line is a holdover from an era before fully graphical operating systems like Windows and OS X were powerful enough to govern every aspect of a computer.


If you’re as old as I am, you may remember having to bust out of the Windows environment back to MS-DOS for one reason or another. You may also remember watching Back to the Future Part II as a newly released VHS tape.


The command-line interpreter itself is called a shell. Like so many other computing words, it’s a metaphor.

Just as some animals, like turtles, have shells that conceal their fragile or dangerous bits, with openings left for the creatures to move around and interact with their environments, a software shell covers and protects the computer—and you, the user—from commands that might cause it to behave erratically or stop working altogether.


Although we’re mostly talking about command-line shells here, the visual interfaces we’re familiar with today are also shells—they’re just shells made of images instead of text.


The opening in our metaphorical shell is the command-line prompt, a string of characters signifying that the computer is ready to receive a command from you. Most of the prompts in this blog will look like this:


$: _ The underscore (_) character here represents the cursor;

the $: is the prompt itself, which signals that the shell is waiting for you to ask it something.


Although shells are our primary means of interacting with some kinds of systems, they’re really just programs. The default shell on OS X and many Linux systems, and the one included in the Git package for Windows is called bash.


Bash is the most widely used command shell for Unix-like computers, and although it’s hardly the only one, it’s the one that, for the purposes of this blog, I’ll assume you’re using.


You’ve probably seen movies or TV shows where hackers in dark sunglasses data-mine mainframes and infiltrate Gibsons, all by typing what appears to be gibberish into computer screens full of more gibberish.


The one thing these movies often get right is how a command prompt works. First, you type the thing you want the computer to do in a language it understands:

$: whoami_


Then you hit Enter to submit the command. In this case, we’ve typed the name of another program we want the shell to run for us. This one, whoami, is very basic: it just tells us what username we’re logged in as.


Once it has finished running, which is typically right away, the program will output (or “print”) a response into the Terminal window below the line where we entered the command, like so:

$: whoami

After that, it’ll print the prompt characters again, ready for another question:

$: _


Command-line programs often lack ceremony. They’ll print out the information you asked for but won’t try very hard to make it pretty or put it into context. Where you might expect to see a label or some contextual information—“Your username is: David”—the whoami command just tells it to you straight.


You asked whoami, it answers David and then gives you a fresh prompt to ask another question. Many commands (both included in the operating system and included with Git) return no response at all.


Although many commands, including several of Git’s, can be almost too chatty with their responses, whoami is terse almost to the point of rudeness. That’s not a sign that anything is wrong.


On the contrary: the lack of response indicates that the system and shell trust you to know what you’re doing. A program finishing (or “exiting”) with no output almost always means that the job you asked of it was done with no errors.


For example, here’s the mkdir command, which creates a new directory:


creates a new directory

$: mkdir javascripts

In case it’s not clear what just happened: mkdir didn’t provide any feedback at all, which could lead you to think that the copy command didn’t work. But watch what happens if we try it again:

$: mkdir javascripts

mkdir: javascripts: File exists

In this case, the command fails for a simple reason: a directory called javascript can’t be created because it already exists.


Command-line navigation

Command-line navigation

The Terminal, like a single Finder window on a Mac, is always looking at a single active directory or folder. This is called the working directory. You can find your current location as an absolute path using the pwd (“print working directory”) command:

$: pwd_



We can do that using the cd (“change directory”) command, which sets a given path as our new working directory—the command-line equivalent of double-clicking on the folder’s icon.


This will often be the first command you run in a new Terminal session, to navigate from your shell’s default starting point (typically your home directory) to the directory containing your Git project.

$: cd Projects/our-website

$: pwd



Here we’ve typed in the path to our project directory (Projects/our website), relative to my home directory, where we started this Terminal session. Relative paths are distinguished from absolute ones by the absence of an initial slash (/), which here represents the root—or topmost—directory of your local file system.


One handy tip to remember if this is your first experience with the Terminal:

you can use the tilde character (~) in paths as an alias for your home directory. The absolute paths /Users/david/Projects/our-website and

~/Projects/our-website are equivalent, and you can use either one to jump to your project directory from anywhere else on your hard drive.


To see a list of all the files and directories inside the current directory, use the ls (“list”) command:

$: ls

css index.html


This particular directory has two things in it: a web page (index.html) and a subdirectory containing our stylesheets (CSS). Unlike in the Finder, there are no icons to differentiate the different kinds of things we’re looking at, like whether CSS is a directory or a file.


But ls gives us a couple of options for requesting more information. For instance, if you tack on the -F option, ls will show trailing slashes to help distinguish directories from regular files:

$: ls –F
css/ index.html
To look inside a directory without navigating into it, we can pass the name of the directory to the ls command as an argument:
$: ls css
Of course, if we need to, we can navigate into a subdirectory of our project using cd.
$: cd css
$: ls
As on the web, the double-dot (..) symbol represents the current directory’s parent, and, as you might expect, it works from a shell prompt
$: ls ..
css index.html
$: cd ..
$: pwd


It’s helpful to remember that the command line and graphical tools like the Finder essentially do the same thing. They both look at the same files on the same hard drive, which means that you can use the Finder (or an app’s Save... dialog) to save files or create directories if you prefer a graphical interface.


If you're handier with the command line or don't like switching back and forth from Git, it may be faster to do almost everything in the Terminal.


On the other hand, depending on your experience and preferences, you may find that you’re able to work more quickly and efficiently using the Terminal and Finder in tandem than by using either one alone.


The prompt

The prompt

To keep things simple, the command prompts used for the remainder of the blog will look like this:

(master) $:

The current Git branch, in this case, master, is listed here in parentheses. Later on, we’ll explore other branches, but the master is the branch we’ll be working on in this blog and the next one.


In our examples, an asterisk after the branch name will indicate uncommitted changes in our working copy, like so:

(master *) $:


Every operating system and shell has its own default prompt format, and if you don’t like the default, you can customize the prompt to look any way you want. It’s very likely that your computer’s prompts and the ones in this blog will not look alike.


I bring this up mainly to reassure you that it’s fine if the examples in this blog and the prompts in your terminal app don’t look exactly the same. The Git commands and their output are what matter most, and those should be consistent no matter what your prompt looks like.




Now that you know how to navigate the Terminal, it’s time to start really interacting with Git by sending it commands, which all look more or less like this:

(master) $: git commandname parameter1 parameter2 »--option


The command name (command name in the example) is one of over 100 individual functions that Git can perform. Behind the scenes, each of these commands is a separate program responsible for its own specific job.


Though some Git functions work with just a command name (like git status), most require some parameters to know how to do their jobs, similar to passing input to functions in a programming language like JavaScript or Ruby.


You can read a lot of Git commands as a kind of sentence: Git, please do a thing to this other thing. For example, the command git checkout master essentially means: “Git, please check out the branch named master.”


Options are special parameters that are denoted by at least one leading dash character. These are rarely required and usually, change something about the default way Git handles a particular task.


Many options have both a long form, like --global, and a shortcut form, like -g. There are also options that take values, like git commit --message="hello world". As we go along I’ll call out important options, what they’re used for, and the kinds of values they take.





Git is a complex beast with hundreds of configuration options, but there are two that it absolutely needs in order to function: your name and email address. Git adds an Author attribute to every commit you make that includes both your name and email address so that your collaborators on a project can know who made a given change.


The name you enter will be used to identify you in changelogs and any other place where Git shows who made a particular change, while your email address not only tells people how to reach you but also tells a hosted service like GitHub who you are on their service.


So let’s tell Git who you are, using the git config command. Unlike most Git commands, which only work inside of a Git project, these can be run from any directory.


Enter each of these lines at a command prompt, filling in your own name and email address:

$: git config --global "David Demaree"

$: git config --global ""


Here, we’re telling Git that a particular configuration property ( should be set to the value (David Demaree) we’ve provided.


The --global option tells Git to set these values as a default for all projects on this computer. You can, if you want to, use the git config command to set configuration options like within specific projects by just omitting --global.


But we’re setting these globally for now because Git requires them to be set somewhere, and this way you won’t have to do it every time you start a new project.


A brief note here about privacy, because sharing personal information such as this can be a sensitive topic. Git will use the name and email address you give it to provide attribution for any commits you make. For local repositories, or commits that haven't been pushed to any server, this information will reside only on your computer.


But commits are meant to be shared, so be aware that commits you share with others (whether it's within your team or on an open source project) will include this information. What’s more, as you'll see, commits are not really meant to be changed after the fact.


I mention this to alert you, not to scare you. While Git requires you to provide a name and email address in order to attribute commits, it doesn't know or care whether the values you enter are your real name or email address.


In fact, pairs of programmers working on the same problem on the same computer will often change their Git setup to claim joint credit for any commits they make.


When you fill out these values, you are free—and expected—to provide only as much information about yourself as you feel comfortable providing.




Once you’ve adopted Git as your version control system of choice, creating a new Git database using the git init command will usually be one of the first things you do.


But it will almost never be the very first thing because most Git repositories are designed to live inside a folder on your hard drive—the working directory—alongside your project files.


More simply: in order to track changes within a project folder, first you need to have a project folder. This may be something you’re already working on and are adding Git too, or it may be something new, for which you want to use Git from the get-go.


For the purposes of this blog, we’ll start developing a new website. First, let’s create a directory to work in. Though we could do this from the Finder or Windows Explorer, in this case, we’ll do it from the command line.


$: mkdir our-website

This creates a new folder called our-website inside of the current directory.

Next, let’s switch into our new directory using the cd command:

$: cd our-website

Now let’s initialize a new Git repository within this new project folder, using the git init command.

$: git init

Initialized empty Git repository in /Users/david/ »work/our-website/.git/

(master) $:


Boom! We now have a fresh, new, empty Git project on our computer. It’s empty both in the sense that it has no files yet—remember, we just created this directory for the first time—and that it has no commits.





If you’re not the person responsible for initiating your project, it’s more likely that your first step upon joining a Git project will be to pull down a copy of the repository stored on a server somewhere. This is called cloning, meaning that what gets saved to your computer is a replica of everything that was stored on the server.


Cloning a repo is a sequential process that Git helpfully wraps in a single command: git clone.


 A sort of macro, git clone is a single, convenient command that performs several related commands at once: it creates a new working directory (named after the repository on the server by default); initializes a new Git repository; adds a remote called origin, and pulls changes from the remote.

$: git clone »our-website.git
Cloning into our-website...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 11 (delta 1), reused 11 (delta 1)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done
$: cd our-website
(master) $:


Following the default of automatically naming the working directory after the repository name is almost always the simplest way to proceed.


But if you do want to give your directory a different name—let’s say you want to prepend a client’s name—you can simply pass a different folder name into git clone as an argument. Here, instead of naming this copy of our client’s project our website, let’s call it client-website:

$: cd ~/Work
$: git clone »clientco-website
Cloning into 'gfh-website'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 11 (delta 1), reused 11 (delta 1)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done
$: cd clientco-website
(master) $:

Either way, once you’ve cloned the remote repo, everything about Git works the same as if you had created the project yourself. What’s more, since Git is generally concerned only about what’s inside your project, not the folder containing it, no matter what name you give when cloning the project, you can safely rename the folder anytime you like.


The clone we’ve just created comes with the full history of this repository, including every change shared by every other copy. This notion of being able to clone not just the working state, but also the entire history of the project, will come in handy later.





Git expects every change you make within a directory under its care to be recorded and stored in the repository for safekeeping—even the change that takes a project from nothingness into being. The phrase commits or it didn’t happen sounds like a joke, but it’s also literally true.


As a practical matter, Git is more concerned with managing your commits than with the files whose changes you’re using commits to track. It’s not exactly that Git is indifferent to the contents of your files; it’s just that its model for organizing and managing your work is oriented around commits.


Commits are a type of Git data called an object. Internally, every piece of information Git knows about—the contents of files, the structure of folders, and, most importantly, the commits that mark the others as versions of a project—is stored as an object, and each particular object has a unique name derived from its contents.


Really, the name—or identifier—of an object is less a name than a short, machine-readable fingerprint that reliably distinguishes objects from one another.


Commits are the only kind of object you’ll work with on a daily basis. Semantically, each commit represents a complete snapshot of the state of your project at a given moment in time; its unique identifier serves to distinguish that state from the way the files in your project looked at any other moment in time.


Git proceeds by addition. Even though files in your project can be created, deleted, or changed, the commits tracking those changes are always added. When you remove a file, you’re adding a commit.


If you change a line of text or code, or even change a file’s name, you’re changing the state of your project, and you’ll add a commit to mark that change and propagate it to the rest of your team.


This is part of the beauty of Git’s design: items in its database are lossless, immutable: they can never truly be changed; only added to. Git is a system of accumulation.


It accumulates every change you tell it about so that you can go back and explore that history later on. When you commit a change to your work, some really cool stuff happens behind the scenes, which we’ll look at in more detail in the next blog. For now, though, let’s make a commitment to see how that works.



project directory

So, we’ve created a directory to hold our website project. Next, let’s add a new file to serve as our homepage. Start a new document in your favorite HTML editor, and add this to it:

<!DOCTYPE html>
<title>Our Website</title>
<h1>Our Website</h1>


Done. Save the file to your project directory as index.html.

Before we move on, let’s ask Git what it knows about the state of our project using the git status command.

(master *) $: git status
# On branch master
# Initial commit
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
# index.html
nothing added to commit but untracked files present (use "git add" to track)


There’s a lot of interesting information here. First, we learn that we’re on our project’s master branch; For now, rest assured that we’re in the right place within the ridiculous multiverse that is Git.


 Because this is a new, empty repository, our next commit will be the initial commit—that is, the very first one on our timeline. Finally, and most importantly, we have a list of “untracked” files, which includes our index.html file.




Before you can commit a file, it must be tracked; before a file can be tracked, you have to add it to Git’s database. That confuses a lot of people who are new to Git because aren’t adding a file and committing it the same thing?


No, they are not! A commit records changes to files in Git’s database—to say that, for instance, a particular file went from version A to version B (or, in the case of the initial commit we’re working on, to define what version A is).


Logically, before Git can know how A has changed to become B, it has to know about versions A or B individually. For the sake of—please don’t laugh —simplicity, it’s normal for us humans to treat commits as a shorthand to represent specific versions of files and folders. But the commits themselves are just references, similar to the way a street address references a house.


When we add a file, we are building the house: the git add command makes a snapshot of the given file and saves it to the repository so that it can be referred to later in a commit.


This means that sometimes Git saves snapshots that will never be committed, and that’s fine. These take up very little disk space, and from time to time, Git will do something called garbage collection wherein it finds objects that aren’t referenced by any commit and deletes them.


If that sounds harsh, think of it this way: you haven’t committed to the versions of your work represented by these stray objects. If they were worth saving, Git assumes you would have committed them somewhere.


By now I’m sure you’re like: “That’s interesting and all, but now how do we get this first version of our file into Git’s database so we can commit it?”


As the git status message suggested, we’ll use the git add command.

To add the homepage file, type this:

(master *) $: git add index.html


Git generally won’t give you any response to tell you anything happened, and once you’ve done this a few times you won’t need one. But since this is your first commit, let’s check git status again:

(master *) $: git status
# On branch master
# Initial commit
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
# new file: index.html


Now we see that our file is no longer listed under Untracked files, but rather under Changes to be committed. We have now staged our file, and we’re ready to commit.

(master *) $: git commit --message "Initial commit"
[master (root-commit) 600df9f] Initial commit
1 file changed, 9 insertions(+)
create mode 100644 index.html
(master) $:


Boom. You just committed your first file.

If you look closely, on the first line of the response, right before the commit message we gave, you’ll see this commit’s unique ID: 600df9f. Most commit IDs you’ll see (and we’ll see quite a few of them as the blog goes on) will look a lot like this one.


The email and name we added earlier tell Git who you are, and the --message option tells your collaborators (or your future self) the nature of the change we just made. While we’re here, let me save you some typing with a little trick.


Many Git command options are available in a shorter (usually single-character) form, denoted by a single dash instead of double dashes. For example, the --message argument can also be typed as -m.


(master *) $: git commit -m "Initial commit"

With that, we’ve made our first step along the long road of the history of this project. Let’s move on to our next change, and our second commit.




Before you can commit a new version of your files, that new version must be added to Git’s database, something we do with the git add command. Another name for this is staging; the staging area is where these new versions live between when you update your working files and when you commit them.


Staging a file causes two things to happen behind the scenes. First, Git saves a snapshot of that file to its database, so that it can be referred to in your next commit.


The nature of Git references is such that a file must already be in the repository for it to be referred to, and it must be referred to in order to commit it. Until a version of a file is staged, Git doesn’t know how to refer to that version, and therefore can’t commit it.


Git also starts a local draft of your next commit, with references to all of the files and directories contained therein—as it happens, including references to files that haven’t even changed, copied over from the previous commit.


Every commit is self-contained: it doesn’t just reference the things that have changed; it references everything that makes up the state of your project at a given moment.


Most of the time you won’t need to know the mechanics of how this works, but I find that understanding what’s going on helps me make better sense of the commit workflow.

Unlike most of the data in your Git repository, the staging area is not synced or shared with anyone else on your team—it lives only on your computer.




This first commit may have seemed like it took a long time, what with me digressing to explain data stores and the semantic nature of Git objects, but as we go on you’ll find that these three commands—status, add, and commit— will make up the bulk of your interaction with Git.


You may use this basic commit workflow dozens of times a day and, barring any especially tricky situations, these three commands will be all you’ll need.


To illustrate this, let’s make our second commit, adding a basic CSS file that we’ll link to from our homepage. First, within your project directory, use the mkdir command to create a new subdirectory called “CSS”. Then fire up your text editor, open a new file, and type:

body {
font-family: 'source-sans-pro', Arial, sans-serif;
font-size: 100%;
Call this file “styles.css” and add it to the css/ subdirectory. Next, let’s link to this new stylesheet from our index.html file:
<!DOCTYPE html>
<title>Hello World</title>
<link href=css/styles.css type=text/css » media=all>
<h1>Hello World</h1>


To recap what just happened in the file system: first we added a directory (CSS/) with one file in it (styles.css); then we changed a file that already existed. Now let’s check git status:

(master *) $: git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
# modified: index.html
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
# css/
no changes added to commit (use "git add" and/or "git commit -a")


Under the heading Changes not staged for commit, we see the HTML file Git already knows about (because we committed it earlier), which Git now (correctly) says has been modified. Below that, the Untracked files list is back;


instead of the styles.css file we added, we see the CSS/ directory that contains it. This is Git’s way of telling us that there’s an entire subdirectory it doesn’t know about.


We can stage both of these changes with a single git add. Here, we’re going to list both our HTML file and the whole CSS/ directory as arguments, separating them with a space to indicate that we want to stage both of the things in this list. In English, it’s like we’re saying, “Git, please add this and this.”

(master *) $: git add index.html css/
Let’s check git status again:
(master *) $: git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# new file: css/styles.css
# modified: index.html


Both files are staged and ready to commit, with Git now noting that index.html has been modified from a previous version, while styles.css (and the CSS/ directory it lives in) are new in this commit. Let’s commit.

(master *) $: git commit -m "Add stylesheet"

With this second commit, your project now has a history. We’ll explore Git’s commit log more in the next blog, but you can already see a timeline taking shape when we run git log.


The process of adding files and then committing them will cover a surprising amount of your version-control needs. And so far, everything makes (relative) sense.


The need to stage files before committing them may seem a little strange but, as we’ll see later, it can also be powerful. At any rate, right now it’s just a small, extra hassle, not a fundamental change in the way we manage files. But other common kinds of changes are less intuitive. We’ll go over some of those next.




When we delete a file in our working copy of a project, it follows that we should also be removing it from our repository. To be sure, Git’s command for deleting files—git rm—does its best to act like it’s simply deleting a file. 


Having said that, let’s recall two things we’ve already learned: that Git is a system of accumulation, and it only cares about changes in the context of a commit. This brings us to our first serious logical paradox in working with Git.


For those of you new to the command line, rm (short for remove) is the standard Unix file-deletion command.


From a command-line prompt, typing rm path/to/my/file will delete the file at that path. git rm behaves in a very similar way, with one added benefit: in addition to deleting the file, it also stages a new commit where the file has been deleted. In other words, in order to remove a file, we have to add a commit.


That last statement may seem a little mind-boggling, so here’s an example that illustrates how files are removed in Git.


Let’s say that since the last time we worked with our web project, someone has added a robots.txt file telling search engines not to index anything on our site. (We’ll go over how other people’s changes get into our repos later. For now, imagine that time has passed and that our project has picked up changes.)


Now, though, we’ve changed our minds and have decided we actually do want to be indexed, so we need to remove the robots.txt file.

To do that, we’ll use git rm:

(master) $: git rm robots.txt

rm 'robots.txt'

[master *] $:


If we run git status, we’ll see that the file’s deletion has been staged for inclusion in the next commit:

[master *] $: git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
# (use "git push" to publish your local commits)
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# deleted:robots.txt


Git’s snapshots, upon which it bases commits, consist of your files’ contents and the directory structures that contain them. When we delete this file using git rm, Git creates a new snapshot of the project minus robots.txt, and stages that version as the next one to be committed. Now let’s commit it:

[master *] $: git commit -m "Remove robots.txt"

[master 983024f] Remove robots.txt

1 file changed, 0 insertions(+), 0 deletions(-)

delete mode 100644 robots.txt


Git vs. trash

Of course, you may be more accustomed to deleting a file by simply dragging its icon into the Trash (or Recycle Bin). Or if you use an all-in-one editing tool like Coda, maybe you delete files by using its built-in file manager.


Even when you know Git has a “remove file” command, it’s hard to overcome years of muscle memory when the delete command you’ve used for so long is right there.


I sympathize with this because even I, a programmer who has used command lines and Git for a really long time, almost always delete files the old-fashioned way (that is, whatever way is most convenient), and then tell Git about the deletion afterward.


As it happens, the command to do this is the exact same one we just used, git rm. If you delete a file yourself, git rm’s job is limited to staging a new change that removes it from the Git index.


So, after deleting our robots.txt file the old-fashioned way, we can just run:

[master *] $: git rm robots.txt

rm 'robots.txt'

Git still gives us a response letting us know it ran the rm 'robots.txt' command to delete the file from our hard drive, even though it didn’t need to. That’s fine—the rm command does nothing if the file has already been deleted.




Next, we want to rename our website’s main stylesheet from styles.css to screen.css (to make space for a separate print.css we might add later). Muscle memory being what it is, we’re liable to use whatever file-renaming command is most familiar. So let’s say we’ve renamed this file in the OS X Finder, and now we check git status:

[master *] $: git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
# deleted:css/styles.css
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
# css/screen. css
no changes added to commit (use "git add" and/or "git commit -a")

We now have an unstaged change deleting our stylesheet entirely. What?


Where did it go?

If you look under Untracked files, you’ll see it there—but Git is telling us that it thinks our renamed file is a totally new, untracked file. This probably seems crazy to a human, but from Git’s extremely literal, name-based perspective on the world (and, more specifically, on your file system), it’s all too logical.


Git is saying that the file it was tracking named “css/styles.css” is no longer present under that name. Meanwhile, it’s also saying that it’s not tracking a file called “css/screen.css” because we haven’t asked it to track a file by that name.


Of course, we know that these are just two names for the same file. But Git doesn’t know that, because Git relies on names to know whether a particular file is familiar to it or not.


It may seem simple or logical to us that this was just a name change, but in order to avoid making a bad assumption about a change that could result in an incorrect commit, Git makes no assumptions when you change things via any method other than a git command.


Because it just seems so easy for Git to take care of this for us, this kind of thing might frustrate you when you first encounter it. But I view it as Git’s simplicity at work.


The job of Git is to track changes and commits. Period. Git can figure out that the deleted file styles.css and the untracked file screen.css have the exact same contents, but it has no idea what that means.


It doesn’t (can’t, really) make the leap required to assume that the two paths are different names for the same file because their being the same file is semantic —it’s meaning that you have ascribed to it, not something intrinsic to the data.


For instance, what if you wanted to create a second copy of this file under a different name? Or what if you deleted styles.css by accident? Git must allow for any of these possibilities, no matter how silly they may seem to us.


But back to our git status: we’re left with one missing file and one mysterious new file. We know they’re the same file, but Git does not. Let’s first try the git mv (short for “move”) command, which is Git’s typical renaming function. Here’s what happens:


[master *] $: git mv css/styles.css css/screen.css

fatal: bad source, source=css/styles.css, destination=css/screen.css


Unlike git rm, which doesn’t care if the deleted file has been deleted or not— and, as we saw, doesn’t even need for the file to actually be deleted—git mv will only rename a file if it is also allowed to move or rename the files in the working copy.


If we’d used git mv to do this initially, instead of the Finder, it would have worked flawlessly. But we didn’t, so now we need to figure out how to stage and commit this change another way.


Git sees two uncommitted changes—a deletion (css/styles.css) and a new addition (css/screen.css)—and we need to address each one individually before we can commit.

First, we’ll use git rm to stop tracking styles.css:

[master *] $: git rm css/styles.css


Then, we’ll use git add to tell it to start tracking the file under its new name:

[master *] $: git add css/screen.css
Once we do both of those, we check our status:
[master *] $: git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: css/styles.css -> css/screen.css
modified: index.html


This is the exact same response we would have seen if we had used git mv, to begin with. Deleting, moving, or renaming files with Git’s built-in commands can save you some typing, but it isn’t necessary. This is an example of a scenario you may encounter while using Git that seems like trouble but really is just annoying.


Depending on how often you rename files, this will either be an incentive to always do things Git’s way, or else will make you feel comfortable doing things in a way you’re familiar with, knowing that you can always explain yourself to Git later.


THE WHAMMY: git add --all

JavaScript behavior

When in doubt—or running short of time—there’s a nuclear option for quickly staging anything and everything that has changed in your local copy: git add --all (or git add -A, for short). The --all option is great for moments when you need to commit several changes at once.


For example, we’ve started to add some JavaScript behavior to our web page, adding a new file (and directory) called js/site.js and linking to it from our HTML document. Here’s our status:

[master *] $: git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working
modified: index.html
Untracked files:
(use "git add <file>..." to include in what will be committed) js/

The js directory (containing our script file, site.js) is new to the project and thus shown as untracked. We also edited index.html to add a script tag linking to the new file; it’s shown here as modified but unstaged.


Normally we would notify Git of these changes one by one, typing in the paths for each file and directory we need to stage. Instead, let’s use git add - -all, then check our status:

[master *] $: git add --all
[master *] $: git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
new file: js/site.js


With that one command, everything we’ve done is ready to commit.

That was a fairly straightforward example, but we can make it more complicated. Before we commit, we decide to rename the directory containing our scripts, from js to the more descriptive scripts, using the mv (“move”) command, and updating our HTML document to reference the JavaScript file under its new name:

[master *] $: mv js scripts
[master *] $: git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
new file: js/site.js
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working
modified: index.html
deleted: js/site.js
Untracked files:
(use "git add <file>..." to include in what will be committed) scripts/

There’s a lot going on here, and to Git’s credit, this complicated status is relatively easy to follow and explain. First, we have the two changes we already staged—these remain staged, even though other changes to the working copy appear to have superseded them.


If we committed right now, our scripts directory would be named js in the repository even though its name is scripted in our working copy.


In addition to the changes we’ve staged, Git also tells us about the newer updates that aren’t yet staged. As before, because it no longer sees the js/site.js file under that name, it’s reported deleted, and the scripts/site.js file that replaces it appears entirely new. We also see index.html listed twice, in two different states at once: modified and staged, but also modified and unstaged.


git add saves a copy of the state of a file in Git’s database for inclusion in a commit; here Git is trying to tell us that there’s an even newer version of index.html than the one we previously staged.

But though this looks like a convoluted mess, git add --all resolve it neatly and quickly:

[master *] $: git add --all
[master *] $: git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
new file: scripts/site.js
All of those seemingly contradictory changes have been condensed into just the two we want. Now we’ll finally commit:
[master *] $: git commit -m "Add JavaScript"
[master 4af326c] Add JavaScript
2 files changed, 1 insertion(+)
create mode 100644 scripts/site.js

That’s great, and much simpler, which begs the question: why not always use git add --all? Frankly, most of the time it’s not only acceptable to use --all, but you’re also almost always better off doing so.


Not only will it save you time, but fewer commands means fewer opportunities to accidentally give Git a wrong signal, leading to confusion and heartbreak.


With this option, you’re telling Git to trust that the version of your project in the working copy is an accurate reflection of what you’d like to commit.


Even so, the fact that you can, but don’t have to, commit everything that has changed in your local copy is one of Git’s most powerful features. This is the power of the staging area: you can precisely control the scope of each commit, making each one broader or more focused, as your working style or the needs of your project demand.


WE’ VE TALKED A LOT SO FAR about “versions”—but what is a version, really? Webster defines it as “a particular form of something, differing in certain respects from an earlier form or other forms of the same type of thing.”


That is, versions can be sequential or iterative—representing the form of a thing as it changes over time—or they can differ in, well, some other way.


The point is that a version isn’t just a copy of a thing, but a copy that differs or has changed in some respect from some other copy.


In Git, sequential versioning—tracking the difference between a snapshot of your work and its earlier forms—is one of a commit’s many jobs. Every commit includes a reference to its immediate predecessor, or parent commit; from that reference, Git can work backward and explain the entire chain of commits that came before it.


In this sense, a commit represents both your work in a particular form and its change into that form from a previous one.


That kind of version relationship is important for understanding where you’ve been, but not always helpful for understanding where you’re going, or why. This is where branches come in handy.


A branch is a virtual copy of your project—a project within your project—where you can make commits freely, in isolation from whatever else may be happening in your repository.


Branches allow us to manage and work with other kinds of versions in Git— experiments, alternate takes, scratch pads—separately from the “official” copy of the work represented by the master branch.


Many people have valiantly tried to explain Git branches by comparing them to things, but no analogy does full justice to these beautiful, powerful buckets of pure information.


The best way to understand branches is on their own terms, as a way of organizing and describing work. And the best way to explain that is for us to dive in and start growing some branches.




Every Git repository starts out with a master branch, to which Git assigns the name master by default. Technically speaking, the master is just a branch like any other; what makes it special is its conventional role as the primary, stable version of whatever project is stored in a repo.


What “primary” or “stable” means is largely up to you, and teams use their master branches in any number of ways. The only thing set in stone about master is that it’s the first branch you’ll work with. It won’t be the last.


Before we create our first new branch, we’ll view a list of all the branches on our local repository using the git branch command:

(master) $: git branch

* master


Here there’s only one branch—master—and the asterisk tells us it’s the current one.

Behind the scenes, a branch is little more than a human-friendly name that points to a particular commit. This particular branch is just a stack of commits, with 18ee782 at the top, or head, of the stack.


The stack is formed by following the head commit’s chain of parent commits as far back as Git can go, all the way to the first commit in this repo.


When talking about branches, it’s tempting to allude to them as places: when you add a commit, the master is the “place” you’re sending it to. As a working metaphor, this is fine most of the time.


Where did I commit the new footer links? is a reasonable question that can often be answered with a branch name. There’s no harm in treating branch names like folders in the metaphorical Trapper Keeper of your project.


It’s not a perfect analogy, of course. While you can only put a sheet of paper in one file folder at a time, Git commits can belong to multiple branches—can be in multiple places—at the same time.


But that’s fine! Ultimately, a branch’s most important role is as a signpost or book mark, pointing you back to a particular version of your work, distinguishing master from, say, another branch named new-homepage.


In this respect, branch names serve the same purpose as labels in Gmail. Just as an email can be labeled as both “Inbox” and “Notes from Mom” simultaneously, so too can a single commit be found in both the master and new-homepage branches.


Branch names aren’t so many destinations as they are labels, or signposts, that help you find certain commits.




For our next project, we’ve been asked to do something pretty significant: redesign our site’s homepage. It may take some time and a lot of commits to getting it right.


But we don’t want to publish our work before it’s done, nor do we want to prevent any of our teammates from making changes to the site or the existing homepage while we’re iterating.


We need a safe place to make potentially big changes without disrupting everything else going on.

The natural place for us to do this work is on a separate branch. In Git lingo, this is called a working branch or topic branch.


Topic branches are distinguished from the master branch in that, well, they have a topic: the work that happens in them has a particular focus or goal, which is typically described by the branch name. Because we’re making a new homepage, let’s create a branch called new-homepage.


To create a new branch, just pass a branch name into the git branch command, like so:

(master) $: git branch new-homepage


This tells Git to create a new branch named new-homepage, using whatever commit you’re currently on as a starting point. It doesn’t matter which branch is set as the current one; git branch only cares which commit is at the head of that branch.


Right now, commit 18ee782 is at the head of the master, so this freshly minted new-homepage branch will also start out with 18ee782 at its head. But these two branches have no relationship to each other, aside from both having 18ee782 as a member.


Annoyingly, Git doesn’t automatically switch you into the new branch when running git branch <branchname>. It creates a new branch, but leaves you with the master set as the current branch, or (in Git terms) checked out. Left in this state, Git will have created your new branch, but your next commit will be to master.


Checking out a branch does two things. First, it resets your working copy to match whatever state is represented by the branch’s head commit. Then, it sets the branch as current so that when you commit, any new commits you add will be added to that particular branch.


To switch branches, you’ll use the git checkout command.

(master) $: git checkout new-homepage Switched to branch 'new-homepage' (new-homepage) $:


While you get used to working with branches, this two-command dance of creating and then switching into branches can quickly grow tiresome, so Git offers a handy shortcut: you can tell git checkout to create and switch to a new branch at the same time by passing in the -b option, like so:

(master) $: git checkout -b new-homepage Switched to a new branch 'new-homepage' (new-homepage) $:

Either way, once you’ve created and switched into a new branch, you should see it when you type git branch:

(new-homepage) $: git branch


* new-homepage

The asterisk tells us that new-homepage has (correctly) been set as the current branch, meaning that our next commits will be sent here.


At the moment, because we haven’t added any commits to either master or new-homepage, the two branches are literally identical—they represent two different names for the exact same commit.


But part of the beauty in how Git handles branches is that most of the time, you don’t need to worry about that: these two things may be literally identical, but they are logically separate. Though master and new-homepage have identical contents, they are nonetheless two different logical copies of your work, not just two names for the same copy.

GIT Branch create

You do need to worry about knowing which branch is the current one because even though these two branches are identical right now, they probably won’t stay that way.


By checking out the new-homepage branch, we’re signaling our intention to diverge from the official timeline and to do some work that may or may not end up in the production version of our code.


Let’s take a moment to savor some semantic details. As I said, the master and new-homepage branches are currently identical. In practice, that means that 18ee782 is the head commit for both branches.


Therefore, it can be said that both branches contain that commit, along with its immediate parent and every commit that directly preceded it, all the way back to the initial commit in this repo.


Since one of the branches we’re talking about is master (and so far we haven’t committed anywhere besides master), the full history of our project is contained within either or both of these branches.


I bring this up because we’re about to make our first commit to a branch other than master. Having checked out the new-homepage branch, whatever we do next will be part of our topic branch, but not part of the master. Not yet.



However grand our plans may be for this new, redesigned homepage, we have to start somewhere. For now, let’s start with something easy: changing the background color on our website header from a blue gradient to flat gray, because flat design.


Let’s make our CSS change and commit it:

(new-homepage *) $: git commit -am "Change background color on header" [new-homepage b26b038] Change background color on header

1 file changed, 1 insertion(+), 1 deletion(-)


Before we proceed, I’d like to call your attention to something. -am (as used in the preceding example) is actually a combination of two other options we’ve seen many times before: -a (to automatically add any changed files to this commit) and -m (specifying the commit message). Most command-line tools allow you to combine multiple options into a single one like this, prepended by a single dash.


The only restriction is that just one of these options (m) can take an argument, and it needs to come last. This particular combination of options is very handy, as in many cases it allows you to commit some changes with only one command.


Having now added this commit, our master and working branches have diverged—the new-homepage branch has one commit that the master doesn’t. The diagram in is more or less how Git sees the current state of our repo. The two branches perfectly overlap, except for that one new commit on the new-homepage branch.


That’s how branches actually work: the new-homepage branch points to b26b038, master points to 18ee782, and b26b038 points to its parent commit, 18ee782. Another way of looking at it is to see the two branches as discrete logical copies of the whole timeline that just happen to be mostly identical.

GIT branch rename

In technical terms, Git not only creates the commit but also moves the pointer for the current branch to the commit we just made. Put a little more simply: when you add a commit on a branch, that new commit supersedes the one that had been there before as the branch’s head commit.


Through the magic of parent commits, we can look backward and trace the lineage of a branch all the way back to the beginning, enabling us to produce graphs showing our two branches’ shared histories. In practice, though, what matters about a given branch most of the time is which commit is on top of the stack.


It matters because the branch’s latest head commit is the one your next commit will be based on. Working on Git branches is a lot like contributing to an exquisite corpse a kind of collaborative art project invented by the Surrealists in the early 20th century, where each contributor sees only the last bits of whatever the previous person added. For example, one person might start a drawing, filling the first third of a sheet of paper.


Then they fold the paper over, covering almost all of their work, before handing it off to someone else, who fills the next section of the paper, and so on. A modern version of the exquisite corpse is Layer Tennis, where two designers pass a Photoshop file back and forth, adding a new layer with every turn.


As you work and collaborate using Git, try not to worry too much about the whole system of commits, branches, and timelines. That stuff can be extremely valuable, and it’s there for when you need it. But in the moment, feel free to focus on just getting from your last commit to your next one, step by incremental step.



There truly are no hard and fast rules about what branches are for; as I mentioned, different teams tend to use branching in completely different ways.


There are some conventions, of course, but even those are fraught with ambiguity. For example, every Git repository has a master branch, and by convention, master is meant to be the “prime” or “default” branch in your project. But it’s up to you to decide what “prime” or “default” means in the context of your work.


Some projects, especially big open-source software projects like Ruby on Rails, use the master branch for all of the bleeding-edge work that’ll go into their next release, periodically spinning off new “stable” branches to finalize and prepare code that’ll end up in actually numbered versions of the Rails framework.


Another approach, more common for websites and web-based applications, is for the master branch to represent the release version of the project that’s deployed to your web servers, possibly many times a day.


What these two kinds of projects often have in common is how they use topic branches. There’s a basic workflow around branching that, even though there is no single correct way to do things, seems to be how teams use Git to get work done most of the time.


First, someone checks out the latest version of master, and from that commit spins off a new branch named after whatever work they’re planning to do, like in the new-homepage branch we created before.


Our new-homepage branch is an example of a topic branch; exploring a new homepage design is the topic, and by branching off were able to explore it freely without worrying about messing up the prime version in master.


Having branched off, we’ll go off and work on our new idea or feature for a while, adding commits to the branch as we go. While all of this is happening, the master can continue to evolve on its own, picking up new commits that aren’t in your new branch.


Nothing about the shared history of the new branch or master will change as a result of work on either branch, and all the new changes on one branch are isolated from changes on the other.


For instance, perhaps a new year is approaching, and you need to update the year in your site’s copyright statement while you’re in the middle of the homepage redesign. After you’ve committed your changes to the new-homepage branch, switching back to master is a git checkout away:

[new-homepage] $: git checkout master Switched to branch 'master' [master] $:


Once you’ve updated the year and committed that change, you can switch back just as easily with git checkout new-homepage.


The name you give to a branch should logically describe its reason for existing. Come up with a pithy label that identifies the work being done.


A branch created to fix a problem with Chrome 32 might be called fix-chrome32-bug;

it could also be named something more specific, like fix-chrome32-webfont-bug, or something more generic like bugfix.


Choose a level of specificity that will distinguish this branch from others on your project without wasting space.

Usable space in branch lists is at even more of a premium than in commit logs (although unlike with commits, it’s possible to delete and clean out old branches that aren’t being used). It’s fine if branch names aren’t completely descriptive—they just need to be descriptive enough.



Sometimes, a branch will serve as a place to do work that you plan to throw away. That’s one of the lovely things about branching in version control systems generally, and it’s especially lovely in Git: branching is quick and cheap, and you’re under no obligation to reconcile the version of your work in a branch with the one in master.


Most of the time, though, people use branches to work on things that they intend to fold back into the master copy eventually. Likewise, as master continues to evolve independently of whatever topic branches you’re working on, you’ll want to synchronize those changes (or at least some of them) with the ones in your branch.


This is so that the version of your website saved in the branch is as fresh as possible—you wouldn’t want to show off a version of your website where the homepage was new but everything else was obviously six months old—but also to ensure that your new work can merge easily back into master when it’s ready.


Merging combines two (or more, but usually two) different branches of your project into a unified version that contains the unique attributes of both. On one side you have your master branch, containing the version of your site that’s currently live on the web.


Someone on your team discovers an error in the contact information, so they create a new topic branch named update-contact-info where the error is fixed. What you want now is a version of the master branch that includes the updated contact info from update-contact-info.


To do that, first check out the master branch on your local copy:

[update-contact-info] $: git checkout master Switching to branch 'master' [master] $:


Then use the git merge command to pull in changes from the other branch into your copy of master:

[master] $: git merge update-contact-info Updating 286af1c..885e3ff Fast-forward


Voilà! You now have a version of the master that contains everything it had before, plus the amended contact info. Let’s step back and examine exactly what just happened under the hood.


What GIT merges are made of

In terms of how Git manages your data, when we say we want to end up with a version of our branch that includes all the stuff from another branch, we have two criteria that need to be met.


First, we want all of the commits we made on the other branch to be visible in our commit log (though their exact order can be flexible). Basically, we need for the history that led us to this moment to still have happened, and for the chain of ancestry to point back through both branches to include all of the work that was done so far.


Second, and more important, we need to end up with a copy of our project’s files and folders that incorporates all of the changes from both branches. It may not surprise you to hear that the outcome of a merge is a commit and, as we’ve seen, every commit represents a complete snapshot of the whole project.


The commit you end up with after a merge is no exception, which means someone or something needs to be responsible for constructing this new, unified version of the project directory.


Most of the time, the “something” responsible for constructing a merged copy of your project is Git, and it has a few strategies it uses to do that.



The simplest, easiest kind of merge is called a fast-forward, which is exactly what it sounds like.


In our example of the updated contact page, nothing had changed in master since we branched off; everything that was different in the update-contact-info branch was stuff that was added after the last commit on our master branch.

Git branch delete

Here, git merge doesn’t have to do any work at all to figure out what the post-merge state of the project should look like, because only one side of the merger has changed.


Therefore, the merged state will be identical to what’s currently in the update-contact-info branch. All Git needs to do is move the master branch book mark from its current commit to the head commit of the other branch.


Following a fast-forward, the two branches simply point to the same commit, making them once again identical in every way except their names.


GIT Merge commits

As smooth and elegant as fast-forwards are, they’re not possible unless only one of the two branches has new commits, which—depending on the size of your team and how quickly things change in your project—may happen only rarely.


The rest of the time, Git falls back to a “true merge,” where it figures out what the combined state of the project should look like, creates a snapshot representing that new, merged version, and finally adds a special kind of commit—a merge commit—to tie everything together.


To return to our contact-info-updating example, let’s say both master and update-contact-info have changed, each picking up one new commit since they branched off.


Because both branches have changed, Git has to do a little work to ensure the two branches can be merged safely.


First, Git identifies changes between the head commits of each branch by looking for the first common ancestor of both versions, before working backward to understand what changed and in what order, using the common ancestor as a reference point.


Then, Git compares each changed file in both branches against the reference point. When Git identifies a line that has changed in either branch, that line is carried forward for inclusion in the final, merged copy. As long as the branches don’t both contain changes to the same line, Git can still merge everything automatically.


Once the merged snapshot has been automatically generated, by default Git seals the deal and commits it for you, with an automatically generated commit message: “Merge branch ‘update-contact-info’ into master.”


This is only a default, of course: you can pass the –no-commit option to git merge to ask Git just to generate and stage the merged-together version of your work, but wait for you to commit it yourself.


One reason you might do this is to craft your own commit message, although the automatically generated one is almost always good enough.


Another reason might be that you want to make some other changes in the same commit, or even to merge in several different branches in a single commit by running git merge --no-commit more than once.


Doing so doesn’t save you any work, though, and you lose the benefit of a merge commit marking when and where a merge occurred. All of this is to say: use the regular, automatic merge unless you have a reason not to.



GIT branch merge

Merge commits have a few unique properties. For example, unlike a normal commit, which has only one parent, merge commits can have two (or more, but usually just two) parents. In most respects, though, merge commits are like any other commit and subject to the same rules.


For a few reasons, it’s generally a bad idea for a topic branch to drift too far away from the latest version of master. Presuming here that topic branches are short-lived, and your ultimate goal is to merge them into master, keeping branches relatively up to date with master will make that eventual, final merge go more smoothly, and reduce the risk of dreaded merge conflicts.


So, from time to time, you’ll want to merge new commits from master into your branch to bring it back up to date:

(new-homepage) $: git merge master 
Updating c7038f8..1c4b16a 
Makefile | 7 ++
Rakefile | 15 ++--


Ideally, the final state of each working branch should differ from master only in ways that are relevant to its topic.


For example, the new-homepage branch should have the changes needed to produce the homepage, but not any unrelated changes, and not any regressions to an older version of master. The same is true of update-contact-info, add-web-fonts, or any other topical branches you might work on.




As we’ve seen, Git can often merge two branches together automatically. But sometimes it’s not obvious how two branches should be merged together, in which case Git will ask for your help.


This is one of the most annoying scenarios in Git, one that can seem really scary the first time you encounter it, but is actually not that bad: the dreaded merge conflict.


Most often, merge conflicts happen when two lines in a merge happen to overlap—that is if two different versions are trying to change the same line of the same file. Under normal circumstances, Git will not try to resolve conflicts itself. Instead, it will do what it can, but after that, it will stop and ask you to finish the merge commit yourself.


The process for resolving a merge conflict is very similar to the process of making a commit. To reinforce an earlier point, merge commits are ultimately just committed. They do have more than one parent, to reflect that they’re combining two prior versions of your project into one unified whole.


Otherwise, though, they follow the same rules as any other commit, including the process for creating them: first you need to stage changes you want to be included; then you commit.


To the extent that merge commits are different, it’s in how much Git will try to do for you automatically before asking you to get involved.


This, perversely, is one of the ways merge conflicts can throw off newcomers: by the time Git says it needs your help, the merge commit is often already mostly staged. It’s as if someone else has started a commit and then left it for you to finish—and indeed, that’s exactly what’s happening.


Upon finding your branch in a conflicted state, if Git is able to successfully merge any files that have changed since the last commit on your current branch, those changes will be added to the staging area automatically.


You generally don’t need to worry about or do anything with these changes; they’re good to go.

This is the part where the doctor says you’ll feel a slight pinch: when Git can’t automatically merge two copies of the same file, it will mark up your working copy and ask you to go through and manually choose which version of each change is the correct one.


And when I say “manually choose,” what I really mean is: Git will fill each conflicting file with arcane-looking gobbledygook called conflict markers, and your job is to go through each one and swap out the marked-up text for the version you want to end up with following the merge.


Let’s dive into an example. One of our colleagues, Meghan, has recently been promoted from Director of Sales to Vice President, and we’re working on a branch (promote-Meghan) to add her new title to the company’s About page.


At the same time, someone else on our team is tweaking the HTML structure for the About page in their own branch (about-page-class-names), using Meghan’s old title but changing all the markup around it. These two changes seem innocuous enough, but they’re a recipe for a Git disaster.


Let’s presume that about-page-class-names are merged into master first so that the new HTML structure is now also part of the master. Then, being conscientious Git users, we try to update our promote-meghan branch by running git merge master:

(promote-meghan) $: git merge master
Auto-merging about.html
CONFLICT (content): Merge conflict in about.html
Automatic merge failed; fix conflicts and then commit the result.
Oh, no! A merge conflict! Now, when we open up about.html, it looks like this:
<div class="team-member">
<h2>Meghan Somebody</h2>
<<<<<<< HEAD <p class="title">Vice President, Market Development</p>
<p class="job-title">Director of Sales</p>
>>>>>>> master


The lines full of angle brackets are conflicted markers, denoting the two versions of the conflicting line, which are separated here by the line full of equal signs. At the top of this conflict section, we have the version of the file in our current branch, identified here as HEAD.


HEAD is Git’s name for the “branch head pointer”; it’s an alias for “the commit at the top of this particular branch,” which in this case means the same thing as “the commit that is currently checked out in this working copy.” This version reflects Meghan’s new title but shows the old CSS class name (class=" title").


The bottom version is the one we’re trying to merge in, identified here by its branch name (master). As you can see, the class name is up to date (class="job-title"), but the actual title is not.


It’s a subtle difference, but big enough for Git not to want to assume anything about which version is right, or how to correctly combine the two. Git doesn’t know anything about job titles, and can’t write HTML. It’s relying on you, its human operator, to step in and craft the merged version.


To resolve this conflict, we need to replace all of this—everything between and including the angle-bracketed lines—with the version of the code that we want to wind up with following the merge:

<div class="team-member">

<h2>Meghan Somebody</h2>

<p class="job-title">Vice President, Market Development</p> </div>


This handcrafted merge incorporates both the new class name and the new title. Next, we’ll officially resolve the conflict by adding the new version to the staging area. Staging this change tells Git that we’ve officially signed off on this version being, well, the official one.

(promote-meghan *) $: git add about.html

We’ll finish the merge by committing, with a message explaining what we did:

(promote-meghan *) $: git commit -m "Merge branch »master into promote-meghan, w/ resolved conflicts"


Although this commit also resolved a merge, it’s just a commit. As mind-blowing as this may sound, you’re under no obligation to use either of the two old versions of this line in this file.


You could decide to resolve the merge by changing Meghan’s title to give her an even bigger promotion to “CEO.” (Don’t be surprised to get a confused email from your actual CEO if you do that.) Whatever version of the file is staged when you make that last commit is the one that will “win” the merge.


If you’re not thorough, it’s possible to accidentally check in a file full of conflict markers, because Git will expect you to know that it’s your job to find and remove them all.


(If you do accidentally check in conflict markup, or anything else that shouldn’t be in a file, do not panic. Just calmly fix whatever got messed up, stage your changes, and commit them. Git may be inscrutable, but at least it’s consistent.)


Again, while merge conflicts are more annoying than truly scary, you’re better off avoiding them whenever possible by merging in master regularly.


This may seem ridiculous given that, in the last example, merging in the master is precisely what tripped us up. But that particular merge conflict was inevitable given the nature of what had changed in the two branches.


By merging in master now, we were able to deal with this one-line conflict when it was small and simple. Later on, we may have picked up other conflicts in addition to this one, and we’d have had to stop in our tracks while we painstakingly cleaned everything up by hand.


The goal of keeping master and topic branches up to date with each other isn’t to prevent conflicts, but rather to make conflicts easier to manage by keeping the differences between two branches small.


Most of the time, if you’re judicious about merging, this kind of thing will never come up. Still, despite your best efforts, merge conflicts sometimes happen anyway.


This scenario, where two different members of your team each make well-intentioned changes that happen to conflict with each other, is always possible. So it can be comforting to know that Git has a solution—and, as always, Git’s solution is a commit.