One of the challenges of learning Git is becoming familiar with its terminology and command structure. Git commands consist of verbs such as
push preceded by the word
git. These base commands are often followed by options that provide more information about how and where Git should act.
Below are some basic git commands and what they do in the git workflow. We’ll use all of them during the workshop and it’s helpful to understand how they work together.
The best way to learn git commands is through practice. In this workshop, we will learn about and go through the following Git workflows:
- Making and maintaining a repository on your machine
- Creating a repository on GitHub
- Syncing local repository with GitHub
- Forking projects and working collaboratively
Table of contents
You can think of a repository as a group of files that Git tracks. When you create a repository Git generates a hidden directory named
.git in the same folder. Information about the repository, changes to the files, and previous versions are all stored in this hidden directory so they are accessible but don’t get in the way.
You can create repositories using GitHub’s web interface, or on your own computer using the command line. Let’s use the command line to create a Git repository for a new project. (On a Mac, open Terminal, on a PC open Git Bash.)
First, create a directory called
hello-world and navigate to the new directory.
$ mkdir hello-world $ cd hello-world
If you’re not sure you’re in the right place use the command
pwd (print working directory) to display the complete path of your current directory.
We will now create a Git repository to track changes to our project. Use the
git init command, as shown below, to initialize a git repository.
$ git init
Initialized empty Git repository in <your file path>/hello-world.git/
hello-world directory is now a git repository.
If you run the
ls command to list the contents of the
hello-world directory, your repository may seem empty. But running
ls -a shows the hidden files in the list, thus, revealing the new hidden directory named
git status command displays the current state of a project.
$ git status
On branch main No commits yet nothing to commit (create/copy files and use "git add" to track)
The output introduces two new Git concepts:
branch main. A Git repository can be split into multiple branches that can be changed and modified independently before merging them eventually. New repositories start with only one branch, named main by default. In this workshop, we mainly work on the main branch.
git commitcommand saves your changes to the repository. The output above tells us there is nothing new to save in our repository.
We will now create and save our first project file. This is a two-stage process. First, we add any files for which we want to save the changes to a staging area, then we commit those changes to the repository. This two-stage process gives us fine-grained control over what should and should not be included in a particular commit and helps us to review the changes.
Let’s create a new file using the
touch command, which is a command used to quickly make an empty file using the terminal.
$ touch index.md
.md extension is for text files written using Markdown, a lightweight markup language with plain text formatting syntax. For more on Markdown see the Mastering Markdown guide.
Let’s check the status of our project again.
$ git status
On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) index.md nothing added to commit but untracked files present (use "git add" to track)
Git has noticed a new file in our directory that we are not yet tracking. With colourised output, the filename will appear in red. To change this, and tell Git we want to track any changes we make to index.md, we use
$ git add index.md
This adds the file to the staging area, telling Git that
index.md is a file that should be tracked. To see the effects of this change on the status of our repository, we run
git status again.
$ git status
On branch main No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: index.md
Note: if you want to add all the modified files to the staging area in a single command, you can use
git add -A.
index.md file now appears under changes to be committed. If you are using colourised output in your terminal, the filename may have changed colour from red to green.
Before committing the new file, open
index.md in a text editor, add a line of text, then save the file. (In this example we enter the text
Hello, world!). Use
git status to see what has changed from Git’s perpective.
$ git status
On branch main No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: index.md 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.md
Note: To remove the files from the staging area, you can use
git reset command.
This lets us know that Git has indeed spotted the changes to our file, but that it hasn’t yet staged them, so let’s add the new version of
index.md to the staging area.
$ git add index.md
Now we are ready to commit our first changes. Commit is similar to saving a file, but in addition to saving the contents of a file,
git commit stores information about the file’s history, including what changes were made, when, and by whom.
$ git commit -m 'Add index.md'
[main (root-commit) e9e8fd3] Add index.md 1 file changed, 1 insertion(+) create mode 100644 index.md
We can see that one file has changed and that we made one insertion, which was a line with the text
Hello, world!. We can also see the commit message
Add index.md, which we added by using the
-m flag after
git commit. The commit message is used to record a short, descriptive, and specific summary of what we did to help us remember the changes without having to look at the them.
If we just run
git commit without the
-m flag, Git will launch the preferred text editor to write a message describing the commit.
Having made a commit, we now have a permanent record of what changed, along with information about who made the commit and at what time.
Note: Before committing your changes to
index.md, you can use the following command to cancel all the changes you made and return the file to the latest commit:
$ git checkout index.md
If you think of Git as taking snapshots of changes over the life of a project,
git add specifies what will go in a snapshot (putting things in the staging area), and
git commit then actually takes the picture and makes a permanent record of it (as a commit). If you don’t have anything staged when you type
git commit, Git will prompt you to use
git commit -a or
git commit --all. This will automatically stage and commit all changes to all files in your repository at once, so use this option with caution.
figure depicted from Library Carpentry: Introduction to Git
Git branches allows you to work on different feature or part of a project collaboratively without impacting the main branch. When the work is complete, the newly created branch can be merged with the main branch. Each branch is a separate version of the main repository.
To see all the branches in your current repository:
$ git branch
We want to make a new branch called
new-feature and add your name to the bottom of
index.md and a new file
$ git branch new-feature
Now, if you run
git branch again, you should see two branches with only one of them active. Git uses the keyword
checkout to switch between branches. The default branch in Git is
main. This branch is not different than any other branch in a Git repository. To move to the newly created branch,
new-feature, we ask git to “checkout” this branch:
$ git checkout new-feature
You can run
git status to check the current/active branch. Some shell themes provide graphical hints to inform you of the current branch at all times.
When your feature is ready, you can merge it with the
master branch. To do so, you first need to checkout to the
$ git checkout master $ git merge new-feature
The output shows the updated files after the merge. When you merge a branch, it does not get removed from the Git tree. To delete a Git branch after merging it with another branch:
$ git branch -d new-feature
.gitignore is the name of a text file in the main folder of your git repository that includes the names of the files and directories that should be ignored by git. Each new line should list a new rule and Git will match the rule with the names of files or folders to find the ones that must be ignored.
For example, in Mac, a
.DS_store file is added by the filesystem to each directory. You can ignore it as well as all files with the extension of
.env and all the files in
_site folder by making a
.gitignore file like below:
.DS_Store *.env _site\
Note: Git is not good in managing binary files. If you are doing frequent updates to your binary files, you might need to solve merging conflicts frequently and
git diff does not return useful information.
git diff command shows the changes we have made before a commit. To test this, open
index.md with any text editor and enter a new line of text (in this example we added It’s a beautiful rainy day on the second line of
index.md). Save the file, then use
git diff to see the changes.
$ git diff
diff --git a/index.md b/index.md index aed0629..989787e 100644 --- a/index.md +++ b/index.md @@ -1 +1,2 @@ # Hello, world! +It's a beautiful rainy day
Here’s what the output reveals:
- Line 1 tells us Git is comparing “a” and “b” versions of the index.md file
- Line 2 indicates which tracked versions “a” and “b” correspond to; “aed0629” and “989787e” are unique computer-generated identifiers for each version
- The last two lines show the changes to the “index.md” between the compared versions:
- there were no changes to the
# Hello, world!line
- a line was added with the text
It's a beautiful rainy day(“+” indicates an addition and “-“, a deletion)
- there were no changes to the
When working with Git, only one branch can be checked out at a time. This is called the “HEAD” branch or “active” or “current” branch. Git makes note of this current branch in the
.git/HEAD file in the Git repository. To find the current
HEAD, you can print this file:
$ cat .git/HEAD
With the following command, you can compare the current state of your files with your latest commit and review the changes applied after your latest commit:
$ git diff HEAD
If you are going to commit the staged files and like to review the changes in the staged files, you can use the following
$ git diff --staged
We can now add and commit the updated version of “index.md”:
$ git add index.md $ git commit -m 'Add note about the weather'
git log command provides another way to view past activity in our git repository.
$ git log
commit 8e2eb9920eaa0bf18a4adfa12474ad58b765fd06 Author: Your Name <your_email> Date: Mon Jun 5 12:41:45 2017 +0100 Add note about the weather commit e9e8fd3f12b64fc3cbe8533e321ef2cdb1f4ed39 Author: Your Name <your_email> Date: Fri Jun 2 18:15:43 2017 +0100 Add index.md
git log lists information about all commits in reverse chronological order, including the commit messages we wrote to describe them. It is important to add meaningful commit messages, especially when working with teams. The best practice is to write commit messages in the imperative (e.g. ‘Add index.md’ instead of ‘Adding index.md’).
To get more information about a particular commit, you can enter the commit hash in front of
git show command.
In some rare cases, for example when you checkout a specific commit or tag or remote branch, the
HEAD file does not contain a branch reference. Your repository is then in a state called Detached HEAD. To solve the issue, you can make a new branch, commit the changes, and merge the changes back to the
The index or staging are is a place to hold the current changes that will be committed when
git commit is executed. It allows you to only commit parts of the working area into the respository.
For example, when you are working on a big feature with a few different subtasks, you can modify the files as you plan, then group the changes and add them separately to the staging area, and finally commit them with appropriate messages focused on the respective change. It also makes reviewing changes more straight-forward.
You want to go back to the previous version in a number of scnearios:
- 1. Undoing Local Changes That Have Not Been Committed: If the changes have not been committed yet, you should check out the file or files that you want to revert back to its previous version:
$ git checkout <filename>
- 2. Undoing Your Last Commit (That Has Not Been Pushed): If you made a mistake on your last commit and have not pushed it yet, you can undo it by moving the HEAD of your repository. For this purpose, run the following commands in the repository directory.
$ git reset --soft HEAD~
Now, your latest commit is undone. The changes remain in place and the files go back to being staged so you can make additional changes or add any missing files and make a new commit.
The common practice is to use at least two branches,
production, for a project. The latest features are implemented in the
development branch and the tested features will be merged into the
Git also supports tags, which you can use to specify a version for the current project. Tags can be viewed as versions for your code and can help you to quickly checkout your project up to a certain commit.
git tag -a v1.0 -m 'This is the first version of my project'
Tags and branches are fundamentally different. A branch always points to the top of the development line and only changes when a new commit is pushed. A tag will not change by making a new commit and will always stay on that version of the code.