This is your brain. This is your brain on computers.

Getting Started with Git and 13 Essential Things to Know

As we’ve discussed in length in our guides to becoming a software developer and some of the best programming tools, source control is a requirement of a successful software product. Without it, you and your team will have no change history, no collaboration ability, and no way of knowing who changed what. That leaves you blind and in the dark.

Love it or hate it, Git is the de facto standard for source control software. It’s critical to learn git if you want to track changes to your code, join a collaborative software project, or contribute to a group project at an enterprise.

Git commands can be a bit unintuitive at first glance, so let’s break down those most likely to be used on a daily basis.

Installing Git

Git works natively on Windows, Mac, and Linux. When using Windows, you’ll most likely be using the tools included in the Git for Windows initiative (Git BASH, Git GUI, and Git Shell Integration). Mac and Linux have native command line support.

To get started, go to git-scm.com/downloads to find the best download for your setup.

For example, with Ubuntu, you’d use: apt install git. You might need to augment this with sudo if your setup requires root to install software.

Note that Git is, by default, purely a command line tool. If you’re interested in using a user interface to manage your Git repositories, check out our tooling guide for some suggestions. You can also check out the official Git GUI page.

The rest of this guide assumes that you’re on Windows, but the commands are identical regardless of your operating system.

From here on out, we will be using Git BASH to issue the various git commands. The $ at the beginning of each line is just a BASH line starter indicator.

Initializing a Local Git Repository

Too Long; Didn't Read Version
git init

Git works by tracking changes to files, folder, and descendants. You can’t do much until you tell Git that you want tracking to be enabled.

If you’re starting a new project or you have an existing project that you want to add to source control, use the following command at the root of whatever folder you want as the top of the repository.

$ git init

Success is indicated by a message like:

Initialized empty Git repository in <path>

You’ll also be able to confirm success by checking for a hidden folder named .git which is a self-containing folder for all Git file history and branches.

Deleting that folder will effectively wipe out the entire repository, all change history, and all branches. Note that this means Git isn’t used on its own as a backup system because there is no redundant storage of file history. This is different from a system like SVN or TFS which is always backed by a remote server, so be careful.

To backup your repositories, you can take advantage of distributed remote services like GitHub, Bitbucket, and Azure DevOps.

Checking the Status of a Git Repository

Too Long; Didn't Read Version
git status

When you initialize a repository, the repository is in a state that reflects whatever the files and file contents were at the time of the command being issued.

To check the current state of the repository, use the git status command.

You should see a printout that can indicate a few things:

  • Nothing to commit
  • Untracked files
  • Changes to be committed
  • Changes not staged for commit
  • Merge conflicts (more on that later)

For example, a “clean” status which means nothing has changed would show like:

$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

We suggest using this command regularly prior to any big operations like merging, pushing, and branching. If you’re always up to date on the current status of your repository, there’s far less of a chance to get yourself into trouble.

Tracking Files and Staging Changes to be Committed

Too Long; Didn't Read Version
git add [file]

By default, files are not tracked by Git and must be explicitly added to Git’s tracked list. This helps prevent auto-generated files (like some compilation outputs) from being added to source control. An “untracked file” is one that didn’t appear in a previous commit and is treated by Git as a new file.

Add New File

For any files that are untracked in the repository (such as those that you’ve added as new), the git status command will reveal a message like: Untracked files: (use "git add <file>..." to include in what will be committed.

$ git status
On branch master
Your branch is up to date with 'origin/master'.

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)

To add a new file to Git’s tracking use the git add command such as git add <file>.

$ git add index.html
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   index.html

Modify Existing File

For files that are already tracked by Git, any time you make a change to that file such as renaming it, deleting it, or updating its contents, Git will flag that as a change.

For changes to tracked files, the git status command will reveal a message like: Changes not staged for commit: (use "git add <file>..." to update what will be committed.

$ git status
On branch master
Your branch is up to date with 'origin/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:   LICENSE

no changes added to commit (use "git add" and/or "git commit -a")

Just like you did for adding new tracked files, the same git add command is used to stage existing tracked files for eventual commit to the repository.

$ git add LICENSE

Using the git status command will then reveal a message like: Changes to be committed.

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   LICENSE

Checking for Differences

Too Long; Didn't Read Version
git diff

Before taking action to commit your changes and enshrine those changes forever in the history of the repository, you should double check your changes to make sure you aren’t accidentally committing anything.

We can’t stress this enough. So many developers blindly commit after blanket adding changes. This causes havoc during code review and merging when files are accidentally added or changes specifically for debugging are not reverted prior to committing.

Ideally you should use a tool like SourceTree, Visual Studio, or Visual Studio Code to visualize the differences. But if you don’t want to or can’t use one of those tools, you can simply use the git diff command.

Example:

$ git diff
 diff --git a/src/SteamWebAPI2.sln b/src/SteamWebAPI2.sln
 index 307ac11..7057e33 100644
 --- a/src/SteamWebAPI2.sln
 +++ b/src/SteamWebAPI2.sln
 @@ -23,3 +23,4 @@ Global
                 SolutionGuid = {65138398-460F-4988-B302-B17E79B250C9}
         EndGlobalSection
  EndGlobal
 +# I added this comment
 \ No newline at end of file

At first glance, the output to this command is a bit weird, but just hang in there. The first half of the example above shows which file is being compared (file before and after changes). The second half shows which line numbers changed and, in this case, which line was added as indicated by the + at the beginning of the line.

The equivalent diff in Visual Studio Code is shown below. Notice that the left window shows the listing of files that changed and the right two windows indicate the before and after change states. It’s a bit more intuitive in our opinion. It’s also a necessity when you’re making large changes across a project. Using git diff alone doesn’t cut it in that case.

Committing Staged Changes

Too Long; Didn't Read Version
git commit -m [message]

After you’ve confirmed that only your expected changes have been added to Git’s staging list, you’re ready to commit those changes.

The simplest way to do so is to use the git commit command with the -m flag like so: git commit -m "This is my commit message". The -m flag will let you type your commit message inline without having to open any external text editors.

If you have a complex commit message, you can type git commit to open, by default, a vim-like interface for advanced text editing. If you aren’t familiar with vim, see this cheat sheet to prevent you from drowning. You will most likely only need to type i to go into insert mode and then :wq to save, quit, and commit to Git.

Once your commit is complete, you can type git log to confirm success by making sure that the top (most recent) commit matches your author, commit message, and date/time.

Managing the .gitignore File

We’ve talked about how Git doesn’t add files to tracking until you explicitly tell it about those files. But what if you never want to track a certain file or an entire type of file? This is common for compilation outputs, libraries, or other runtime dependencies that would typically be restored by each developer individually outside the repository.

There’s a file specifically for this scenario! At the root of your repository, you can create a file named .gitignore which contains a listing of file names, paths, and wildcards that should be ignored by Git.

Creating this file can be tricky on Windows. See this StackOverflow answer for the quickest way to do it.

If you’re using Windows it will not let you create a file without a filename in Windows Explorer. It will give you the error “You must type a file name” if you try to rename a text file as .gitignore.

You can get around this Windows Explorer error by appending a dot to the filename without extension: .gitignore. will be automatically changed to .gitignore

StackOverflow answer

We suggest using a predefined and crowdsourced .gitignore file for whatever language, framework, or SDK you are using. If you have files that you want to track that aren’t included in those crowdsourced examples, you can obviously edit them to your heart’s content.

Note that .gitignore files don’t retroactively remove files from your repository’s history. If a file is currently tracked by Git, it will continue to be tracked even if you add that file to the .gitignore file. To fix this, you’ll need use this command: git update-index --skip-worktree <file>. This will update the index or directory cache of the file to tell Git that it may not need to be tracked anymore.

Creating Branches

Too Long; Didn't Read Version
git branch [branch_name]

git checkout [branch_name]


As we previously wrote about in 5 Essential Things Every Programmer Should Know, branching is critical to your success, your team’s success, and ultimately your project’s success.

Without the ability to create independent branches of code, team members would be conflicting with each other every time a developer made a commit. Since we also advocate for committing early and committing often, such conflicts would be an immediate deal breaker.

Let’s reference the image above and show how to create the feature-1 branch using Git commands.

The black line and dots is the master branch (line) and each commit on that branch (dot). “Master” is the name of the default branch when you initialize a repository. Another name for this branch is the “trunk.”

Let’s create a feature branch to work on a new feature which is isolated from the master branch.

$ git branch feature-1

After the branch has been created, you need to switch your current branch to it so that all changes will be made on that branch.

$ git checkout feature-1
Switched to branch 'feature-1'

Now we can check to make sure the switch was successful.

$ git status
On branch feature-1
nothing to commit, working tree clean

All changes and commits will now be made on the feature-1 branch until we switch back to master.

Merging Branches

Too Long; Didn't Read Version
git checkout [destination_branch_name]

git merge [source_branch_name]


Branches are nearly useless if kept in isolation forever. Team members would be working on features that never get merged together. The application would then have its code split across a ton of different branches!

That’s where merging comes in. Git makes this fast and easy.

Use the following commands when you’re ready to merge your changes back into something like the master branch so that everyone else on your team can start seeing your changes.

Switch to the destination branch. This is the branch that you want to merge the changes into.

$ git checkout master

Confirm that the switch was successful.

$ git status
On branch master
nothing to commit, working tree clean

Now merge changes from your feature branch into the current branch (in this case, master). Remember, you’re merging from a destination branch (like a feature branch) into whatever branch is currently checked out (like master, confirmed via git status).

$ git merge feature-1
Updating 02a0825..3fb0fd3
Fast-forward
README.md | 3 ++-
1 file changes, 2 insertions(+), 1 deletion(-)

And there you have it. Your changes are now included in the master branch ready to be rolled out to the masses.

Handling Merge Conflicts

Too Long; Didn't Read Version
git checkout [destination_branch_name]
git merge [source_branch_name]
[resolve merge conflicts as necessary]
git add [file]
git commit -m [message]

When multiple people are working on the same project, there are inevitably conflicts (yes, even with good branching and merging). That’s because there are sometimes new features and fixes that may overlap with code that was changed by someone else on a different branch.

So what if you edit line 13 in README.md, and your coworker also edits line 13 in README.md on a separate branch? Who wins? Maybe you could flip a coin, but that definitely wouldn’t be an accurate reconciliation.

Instead, we need to enlist a good strategy to resolve the conflicts so that everyone is happy.

The simplest way to resolve conflicts is by opening the affected files and reconciling the differences manually.

For example, let’s say both you and a coworker modified the README.md text file. When you merge master (coworkers changes) into your feature branch, Git complains that some files are now in conflict.

$ git status
On branch feature-1
nothing to commit, working tree clean

$ git merge master
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

So now let’s see what the conflicted file looks like.

1     change 1
2     change 2
3     <<<<<<< HEAD
4     your change here
5     =======
6     coworker's change here
7     >>>>>>> master

Git modified the file with some special lines to indicate the conflicting details.

  • <<<<<<< HEAD indicates the starting point of your changes.
  • >>>>>>> [source_branch_name] indicates the end point of your coworker’s changes.
  • ======= is a separator for the conflicting changes.

You have some choices to make now, and that may involve a discussion with your coworker.

  1. Use your changes and abandon your coworker’s changes. Accomplished by removing the separator line and everything below it.
  2. Use your coworker’s changes and abandon your changes. Accomplished by removing your changes and all the Git-added marker lines.
  3. Use both changes in a specific order. This requires understanding which order the changes should be in, arranging them in that order, and then removing the Git-added marker lines.

And finally, after you’ve resolved the conflicts to your liking, you need to stage and commit your changes.

$ git add [file]
$ git commit -m [message]

Managing Remote Repositories

Up until this point, this guide has only made changes to a local repository. That’s great, but isn’t very useful if you want to contribute to a project in which developers across the world are also contributing. Distributed services like GitHub and BitBucket are great examples of services that provide remote repositories for team collaboration.

The terminology used to manage remotes can be a little confusing, so let’s break it down.

First, we need to get a remote repository from something like GitHub. This process is called “cloning” a repository.

Let’s clone the repository for the Angular framework from GitHub. This will pull down the current state of the master branch into whatever directory is active in your shell and create a local repository.

$ git clone https://github.com/angular/angular.git

After cloning, we can check the status of any remote repositories that are linked to the new cloned local repository.

$ git remote -v
origin https://github.com/angular/angular.git (fetch)
origin https://github.com/angular/angular.git (push)

A remote repository is already linked with our local repository because we cloned it from GitHub. Notice that Git has named the default remote repository as “origin”. That terminology makes sense when you consider that the repository originated from GitHub (remote by definition).

When working on a distributed product (like Angular that we cloned above), it’s extremely common to consistently push and pull changes to and from remote repositories. You’ll be working in your local clone of that repository, but a reconciliation will need to happen against the remotely linked repository.

When contributing, you will typically follow this pattern:

  • Stage changes
  • Commit changes locally
  • Push changes to a remote
Further Reading
See our expansive guides to GitHub and Bitbucket pricing to find the best fit for your team’s next project.

Pushing Changes to a Remote Repository

To push any commits that you have locally, use the following commands making note of the name of the remote repository to push against and the branch in which you want your changes included.

$ git push [remote] [branch]
$ git push origin master

Pulling Changes from a Remote Repository

To fetch or pull any commits that you need to merge locally, use the following commands making note of the subtle difference between the two.

First, you can fetch changes from the remote repository but will be required to manually merge any changes into your local work.

$ git fetch origin

More conveniently, you can pull changes from the remote repository and have those changes automatically merged into your local work.

$ git pull origin

Further Reading: Once you’ve mastered the basics of Git, read through our ultimate guide to building a portfolio on GitHub.

Justin Skiles

Justin Skiles

Justin has been developing enterprise application software for over 10 years primarily using Microsoft stacks, Azure, and various open source tools. He has most recently been trying his best as a Manager and Director of Software Engineering in the health care industry.

Share the Knowledge

Share on facebook
Facebook
Share on twitter
Twitter
Share on linkedin
LinkedIn
Share on pinterest
Pinterest
Share on facebook
Share on twitter
Share on linkedin
Share on pinterest

Follow our updates

JOIN OUR SUBSCRIBERS

GET FREE UPDATES

Keep Exploring. Choose Your Path.

Be a Better, Smarter You

With our in depth guides, you’re bound to be setup for success.

Our experts have been collectively developing software for over 20 years.

We find the best tools and direct you to them so that you don’t have to.

JOIN THE CONVERSATION

Get the latest ultimate guides, tutorials, and advice to level up your skills.