Git Series — 2. Git Basics
In previous article “Introduction & Getting Started With Git” of Git series, we went over definition of Git, Its basic features, basic flow, its advantages, difference between Git and GitHub and finally how to install Git on different Operating Systems. In this article, we will cover basics of Git starting from creating a repository to cloning an existing repository, tracking and committing changes, undoing changes, pushing and pulling changes ,managing remote branches, Tagging and Aliases. So let’s start..
Creating a new repository
Creating a new repository is simplest task in git. You just need to open a CLI tool according to your Operating System (terminal in Linux or Command Prompt/Powershell/Git Bash in Windows), cd to the directory where you need to create the repository like
for Linux:
$ cd /home/user/my_project
for macOS:
$ cd /Users/user/my_project
for Windows:
$ cd C:/Users/user/my_project
and type:
$ git init
This creates a new hidden subdirectory named .git
that contains all of your necessary repository files — a Git repository skeleton.
Cloning an existing repository
If you want to clone an existing repository instead of creating a new one you can use
git clone REPO_URL
For cloning a repository from GitHub, you can find REPO_URL from the screenshot below
So, you can clone above repository by typing
git clone https://github.com/NomanNasirMinhas/Rust-Calculator.git
It will clone this repository in you current working directory opened in terminal.
As mentioned in previous article, you can use any server hosted repository as well instead of GitHub. So to use repository from your server you can type
git clone user@server:path_to_repo/repo.git
At this point, we have just created or cloned a repository and nothing in your project is tracked yet.
Tracking and Committing Changes
Ignoring Files
Before moving to tracking and committing changes we need to know about ignoring files. Often, you’ll have a class of files that you don’t want Git to automatically add or even show you as being untracked. For example node modules
folder in nodeJS projects or target
folder in rust projects. In such cases, you can create a file in project parent folder listing patterns to match them named .gitignore
. Here is an example .gitignore
file:
# ignore all .a files
*.a
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the TODO file in the current directory, not subdir/TODO
/TODO
# ignore all files in any directory named build
build/
# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt
# ignore all .pdf files in the doc/ directory and any of its subdirectories
doc/**/*.pdf
GitHub maintains a fairly comprehensive list of good
.gitignore
file examples for dozens of projects and languages at https://github.com/github/gitignore if you want a starting point for your project.
Staging files
When you have added, edited or deleted any file in your project and you want to track the changes in git you can use “git add” command with different approaches according your needs.
To track changes in a specific file just type
git add file_name.file_extension
To track changes in files of a specific extension use
git add *.file_extension
Incase there are a lot of files and you can’t add all files individually then track changes in all files contained in the folder use by
git add .
You can use multiple add commands simultaneously. For example to track changes of a specific exe file and all files of txt extension you can use
git add file_name.exe
git add *.txt
In order to check current status of all tracked files you can use “git status” for example
$ git add README
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
Removing Files
To remove a file from Git, you have to remove it from your tracked files (more accurately, remove it from your staging area) and then commit. The git rm
command does that, and also removes the file from your working directory so you don’t see it as an untracked file the next time around e.g
rm PROJECTS.md
Another useful thing you may want to do is to keep the file in your working tree but remove it from your staging area. In other words, you may want to keep the file on your hard drive but not have Git track it anymore. This is particularly useful if you forgot to add something to your .gitignore
file and accidentally staged it, like a large log file or a bunch of .a
compiled files. To do this, use the --cached
option:
git rm --cached README
Committing Changes
After adding all files you need to need to commit these changes by
git commit -m "Describe Your changes in a sentence or any other message"
Adding the -a
option to the git commit
command makes Git automatically stage every file that is already tracked before doing the commit, letting you skip the git add
part
git commit -a -m 'Add new benchmarks'
After you have created several commits, or if you have cloned a repository with an existing commit history, you’ll probably want to look back to see what has happened. The most basic and powerful tool to do this is the git log
command. Most clean way to do this is
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 Change version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Remove unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 Initial commit
Undoing Commits
One of the common undoing commits is required when you commit too early and possibly forget to add some files, or you mess up your commit message. If you want to redo that commit, make the additional changes you forgot, stage them, and commit again using the --amend
option eg
git commit -m 'Initial commit'
git add forgotten_file
git commit --amend
Unstaging Files
For example, let’s say you’ve changed two files and want to commit them as two separate changes, but you accidentally type git add *
and stage them both. How can you unstage one of the two? The git status
command reminds you:
$ git add *
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: CONTRIBUTING.md
renamed: README.md -> README
Right below the “Changes to be committed” text, it says use git restore --staged <file>…
to unstage. So, let’s use that advice to unstage the CONTRIBUTING.md
file:
$ git restore --staged CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: README.md -> READMEChanges not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
The CONTRIBUTING.md
file is modified but once again unstaged.
Unmodifying a Modified File
What if you realize that you don’t want to keep your changes to the CONTRIBUTING.md
file? How can you easily unmodify it — revert it back to what it looked like when you last committed (or initially cloned, or however you got it into your working directory)? Luckily, git status
tells you how to do that, too. In the last example output, the unstaged area looks like this:
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: CONTRIBUTING.md
It tells you pretty explicitly how to discard the changes you’ve made. Let’s do what it says:
$ git checkout -- CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage) renamed: README.md -> README
You can see that the changes have been reverted.
Pushing/Publishing Our Commits
Please keep in mind that up-to now our changes locally and have not yet been publish to cloud/server repository.
In order to publish your changes we just need to type
git push
or
git push <remote> <branch>
to be more specific if you have multiple remotes or multiple branches.
We will get into details of “remotes” in next section of this article and “branches” in next article of this series.
At this stage, if you had made changes in a cloned repository which you owned, this command will publish your changes but you had created a new git repository then at this point push
command will throw an error of upstream. This error means that we have not yet connected our local repository to a remote folder where changes are to be pushed. This brings to our next topic of “remotes”
Remotes
To be able to collaborate on any Git project, you need to know how to manage your remote repositories. Remote repositories are versions of your project that are hosted on the Internet or network somewhere.
It is entirely possible that you can be working with a “remote” repository that is, in fact, on the same host you are. The word “remote” does not necessarily imply that the repository is somewhere else on the network or Internet, only that it is elsewhere.
Checking Remotes of a git repository
To see which remote servers you have configured, you can run the git remote
command. It lists the short names of each remote handle you’ve specified. If you’ve cloned your repository, you should at least see origin
— that is the default name Git gives to the server you cloned from.
You can also specify -v
, which shows you the URLs that Git has stored for the short name to be used when reading and writing to that remote.
$ git remote -v
origin https://github.com/schacon/ticgit (fetch)
origin https://github.com/schacon/ticgit (push)
Results of above command show 2 remotes with same name but fetch and push. It means the url from which changes are pulled (local code is synced according to a remote code) and to which changes are pushed respectively. If we run this command in a new created git repository, then we won’t be able to see any remote. In order to add a remote your local repository we can use
git remote add <shortname> <url>
for example
git remote add origin https://github.com/paulboone/ticgit
https://github.com
in above command shows that we have created our remote repository at GitHub. We can add remote repository of any other server as well by
`git remote add origin ssh://user@host:/path_to_repository
Now that we have add a remote to our local repository, we can push our changes by
git push origin master
Once we have added a remote we don’t need to add it again when pushing our commits.
Renaming and Removing Remotes
You can run git remote rename
to change a remote’s short name. For instance, if you want to rename origin
to repo1
, you can do so with git remote rename
:
$ git remote rename origin repo1
Pulling from Your Remotes
Suppose that you pushed your changes to a remote. Your other teammate working on the project cloned your repository. While he was going through the code, he saw some bugs and he made some changes in it to fix them. He then pushed changes. How will you get the changes made by your friend in your local code? You can run pull command to fetch latest code on the remote repository by
git pull
This swap your local code with the code on remote repository provided that there is no conflict between the two codes. By conflict, it means that you have made any changes to code in any file after pushing it to the remote. It might be the case that when you pull code, your code might have some uncommited local changes in code which might conflict with the code being pulled from remote. In this case we have merge issue because git can not decide which code should be kept; local or remote. Hence it is good practice to always pull code first and then make changes in it locally.
Tagging
Git has the ability to tag specific points in a repository’s history as being important. Typically, people use this functionality to mark release points (v1.0
, v2.0
and so on). Git supports two types of tags: lightweight and annotated. A lightweight tag is very much like a branch that doesn’t change — it’s just a pointer to a specific commit. Annotated tags, however, are stored as full objects in the Git database.
Creating an annotated tag in Git is simple. The easiest way is to specify -a
when you run the tag
command:
git tag -a v1.4 -m "my version 1.4"
You can see the tag data along with the commit that was tagged by using the git show
command:
$ git show v1.4
tag v1.4
Tagger: Ben Straub <ben@straub.cc>
Date: Sat May 3 20:19:12 2014 -0700my version 1.4commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700 Change version number
Another way to tag commits is with a lightweight tag. This is basically the commit checksum stored in a file — no other information is kept. To create a lightweight tag, don’t supply any of the -a
, -s
, or -m
options, just provide a tag name:
$ git tag v1.4-lw
This time, if you run git show
on the tag, you don’t see the extra tag information. The command just shows the commit:
$ git show v1.4-lw
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700 Change version number
By default, the git push
command doesn’t transfer tags to remote servers. You will have to explicitly push tags to a shared server after you have created them. This process is just like sharing remote branches — you can run git push origin <tagname>
.
$ git push origin v1.5
If you have a lot of tags that you want to push up at once, you can also use the --tags
option to the git push
command. This will transfer all of your tags to the remote server that are not already there.
$ git push origin --tags
Listing the existing tags in Git is straightforward. Just type git tag
(with optional -l
or --list
):
$ git tag
v1.0
v2.0
To delete a tag on your local repository, you can use git tag -d <tagname>
. For example, we could remove our lightweight tag above as follows:
$ git tag -d v1.4-lw
Deleted tag 'v1.4-lw' (was e7d5add)
Note that this does not remove the tag from any remote servers. Most intuitive way to delete a remote tag is with:
$ git push origin --delete <tagname>
Git Aliases
If you don’t want to type the entire text of each of the Git commands, you can easily set up an alias for each command using git config
. Here are a couple of examples you may want to set up:
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
This means that, for example, instead of typing git commit
, you just need to type git ci.
In Next Article we will go through a basic and simplified flow of git from creating an empty git repository to pushing and pulling code.