DEV Community

loading...

Master Git in 7 minutes

Valeria
Re-inventor extraordinaire, node/js lover, optimisation addict and open web advocate
・Updated on ・7 min read

Essentially, Git keeps tabs on text changes, but the definition is a version control system. Chances are you've already used git one way or another: it is a de-facto standard for code versioning due to it's distributed nature, as opposed to centralised Apache Subversion (SVN).

Installing Git

To check if you have Git installed run in your terminal:

git version
# git version 2.30.1 (Apple Git-130)
Enter fullscreen mode Exit fullscreen mode

If you don't have it, follow instructions on https://git-scm.com/downloads. Mac users can install it with brew: brew install git

Configuring Git

There are just a few things we want to configure:

git config --global user.name "John Doe" && # your name
git config --global user.email johndoe@example.com && # your email
git config --global init.defaultbranch main # default branch name, to be compatible with GitHub
Enter fullscreen mode Exit fullscreen mode

You can see current global configuration with:

git config --global --list
# Type ":q" to close
Enter fullscreen mode Exit fullscreen mode

Git stores configuration in plain text and, if you prefer, you can edit global configuration directly in ~/.gitconfig or ~/.config/git/config.

As the command suggests, removing --global would make these commands scoped to the current folder. But to test that out we need a repository.

Creating new repository

A repository is just a folder with all the stuff you want to track. To create one run:

mkdir gitexample && 
cd gitexample && 
git init
# gitexample git:(main)
Enter fullscreen mode Exit fullscreen mode

This command creates a folder .git inside gitexample folder. That hidden .git folder is what makes a repository: all local configuration and changes are stored there.

Making changes

Let's create something in the repository:

echo "Hello, Git" >> hello.txt
Enter fullscreen mode Exit fullscreen mode

If we ran git status, we'll see the newly created untracked file:

git status
# On branch main
# 
# No commits yet
# 
# Untracked files:
#  (use "git add <file>..." to include in what will be committed)
#   hello.txt
#
# nothing added to commit but untracked files present (use "git add" to track)
Enter fullscreen mode Exit fullscreen mode

As the output suggests, let add the file. It can be done directly with:

git add . # Or `git add hello.txt`, if we don't want all files
Enter fullscreen mode Exit fullscreen mode

If you check on the repository status now, you'll see that the file is added (aka staged), but not yet committed:

git status
# On branch main
# 
# No commits yet
# 
# Changes to be committed:
#  (use "git rm --cached <file>..." to unstage)
#   new file:   hello.txt
Enter fullscreen mode Exit fullscreen mode

To record the changes, let's commit them:

git commit -m "Add hello.txt"
# [main (root-commit) a07ee27] Adds hello.txt
# 1 file changed, 2 insertions(+)
# create mode 100644 hello.txt
Enter fullscreen mode Exit fullscreen mode

Pro tip: git commit -m <MESSAGE> is a short hand command, you can use git commit to open editor (mostly vim) and provide a detailed commit description instead.

Let's check the changes with:

git log
# type :q to close
Enter fullscreen mode Exit fullscreen mode

It will show something like:

commit a07ee270d6bd0419a50d1936ad89b9de0332f375 (HEAD -> main)
Author: Your Name <your@email.address>
Date:   Sun Jul 11 11:47:16 2021 +0200

    Adds hello.txt
(END)
Enter fullscreen mode Exit fullscreen mode

Creating branches

Having a separate version of the initial code can be useful in a lot of situation: e.g. when testing out a feature you're unsure about or to avoid code conflicts when working together. That's exactly what a git branch is: it grows from a particular point in history.

To create a branch run git branch NAME and to switch branch run git checkout NAME. Or simply:

git checkout -b dev # switches to a new branch called "dev"
# Switched to a new branch 'dev'
# gitexample git:(dev)
Enter fullscreen mode Exit fullscreen mode

Let's change something in the hello.txt file and commit the changes:

echo "\nHello, Git Branch" >> hello.txt &&
git commit -am "Change hello.txt"
Enter fullscreen mode Exit fullscreen mode

Now let's switch back to main version:

git checkout main &&
cat hello.txt
# Switched to branch 'main'
# Hello, Git
Enter fullscreen mode Exit fullscreen mode

As you can see, the file contents are still the same as they were. To compare branches we can run:

git diff dev
# diff --git a/hello.txt b/hello.txt
# index 360c923..b7aec52 100644
# --- a/hello.txt
# +++ b/hello.txt
# @@ -1,3 +1 @@
# Hello, Git
# -
# -Hello, Git Branch
# (END)
# type ":q" to close
Enter fullscreen mode Exit fullscreen mode

Let's make changes in main branch as well:

echo "\nHi from Main Branch" >> hello.txt &&
git commit -am "Change hello.txt from main"
# [main 9b60c4b] Change hello.txt from main
# 1 file changed, 2 insertions(+)
Enter fullscreen mode Exit fullscreen mode

Now let's try to combine the changes:

git merge dev
# Auto-merging hello.txt
# CONFLICT (content): Merge conflict in hello.txt
# Automatic merge failed; fix conflicts and then commit the result.
Enter fullscreen mode Exit fullscreen mode

Because the file was changed in the same place twice we got a conflict. Look at the file:

cat hello.txt
<<<<<<< HEAD
Hello, Git

Hi from Main Branch
=======
Hello, Git
>>>>>>> dev
Enter fullscreen mode Exit fullscreen mode

There is also a tool to see changes separately:

git diff --ours # :q to close 
git diff --theirs #:q to close
Enter fullscreen mode Exit fullscreen mode

You can manually edit the file and commit the changes, but let's imagine we only want one of the versions. We'll start with aborting merge:

git merge --abort
Enter fullscreen mode Exit fullscreen mode

And restarting merge with "theirs" strategy, meaning that in case of conflict we'll use whatever incoming branch insists on:

git merge -X theirs dev
# Auto-merging hello.txt
# Merge made by the 'recursive' strategy.
# hello.txt | 5 +----
# 1 file changed, 1 insertion(+), 4 deletions(-)
Enter fullscreen mode Exit fullscreen mode

The opposite to this strategy is "ours". Merging both changes together will require manual editing (or use of git mergetool).

To see list of all branches run:

git branch # type :q to close
#  dev
# * main
Enter fullscreen mode Exit fullscreen mode

Finally, to delete the branch run:

git branch -d dev
# Deleted branch dev (was 6259828).
Enter fullscreen mode Exit fullscreen mode

Rebasing branches

Branches "grow" from a particular point in git history, rebase allows to change that point. Let's create another branch and add some changes to hello.txt once more time:

git checkout -b story &&
echo "Once upon a time there was a file">>story.txt &&
git add story.txt &&
git commit -m "Add story.txt"
# Switched to a new branch 'story'
# [story eb996b8] Add story.txt
# 1 file changed, 1 insertion(+)
# create mode 100644 story.txt
Enter fullscreen mode Exit fullscreen mode

Now, let's come back to the main branch and add changes there:

git checkout main &&
echo "Other changes" >> changes.txt &&
git add changes.txt &&
git commit -m "Add changes.txt"
Enter fullscreen mode Exit fullscreen mode

To replay the changes we made in main to story branch run:

git checkout story &&
git rebase main
# Successfully rebased and updated refs/heads/story.
Enter fullscreen mode Exit fullscreen mode

You can see new file created in main branch being added to story branch:

ls
# changes.txt hello.txt   story.txt
Enter fullscreen mode Exit fullscreen mode

Word of caution: do not rebase branches that someone else might have used, e.g. the main branch. Also, keep in mind that every history manipulation on a remote repository will require forcing these changes to take effect.

Remote repository

If you haven't yet, create a GitHub account, login and create a new empty repository (private or public).

Assuming the repository name was "example" run the following command (change to your username):

git remote add origin git@github.com:USERNAME/example.git &&
git push -u origin main
Enter fullscreen mode Exit fullscreen mode

You can refresh the page and see files in main branch. To push all local branches to remote repository run:

git push --all origin
Enter fullscreen mode Exit fullscreen mode

Let's edit something on GitHub: just click any file and the pencil icon. Add a line with any text you want and press "Commit changes".

Now run this command locally to get the remote changes:

git checkout main &&
git pull
Enter fullscreen mode Exit fullscreen mode

Managing uncommitted changes

If you want to save your local changes for later you can use git stash:

echo "Changes" >> hello.txt &&
git stash
Enter fullscreen mode Exit fullscreen mode

Now you can use following command to check, apply or discard these changes:

git stash list
# stash@{0}: WIP on main: 92354c8 Update changes.txt
git stash pop # to apply changes
git stash drop # to drop changes
Enter fullscreen mode Exit fullscreen mode

Pro tip: you can use stash number, i.e. git stash pop 0 to apply a particular stash or git stash drop 0 to drop it.

If you want to discard all local changes and simply restore repository to last committed changes run:

git restore .
Enter fullscreen mode Exit fullscreen mode

Managing committed changes

Once you create a commit, this change is saved in local git history. As mentioned before, all changes affecting remote history would require a git push --force. Keep it in mind for all following commands.

Let's start with editing the last commit message :

git commit --amend # type :wq to save and close
# Press "i" to edit, "Esc" to stop editing
Enter fullscreen mode Exit fullscreen mode

How about we reset everything to the very beginning?
To find the ID of the very first commit run this command and scroll (with arrow down) to the very end:

git log --abbrev-commit
# commit a07ee27
# Author: Your Name <your@email.address>
Date:   Sun Jul 11 11:47:16 2021 +0200

    Adds hello.txt
(END)
# type ":q" to close
Enter fullscreen mode Exit fullscreen mode

Now run this to reset the repository, but keep all changes unstaged:

git reset --soft COMMIT # e.g. a07ee27
Enter fullscreen mode Exit fullscreen mode

As opposite to it, you can also make a hard reset and get rid of all the changes with git reset --hard COMMIT. There are several other types of reset that you can learn from git documentation

Aliases

Most of the times you'll be using just a handful of command (checkout, add ,commit, pull, push and merge mostly), but are some things you might want to have around for "just in case".

One way to store those are git aliases. To configure an alias just set it in a config. For example, one alias I use a lot is git tree, it prints a nice history log in a form of a tree:

git config --global alias.tree 'log --graph --decorate --pretty=oneline --abbrev-commit'
# Try it with `git tree`
Enter fullscreen mode Exit fullscreen mode

Another useful alias deletes all merged branches:

git config --global alias.clbr '!git branch --merged | grep -v \* | xargs git branch -D' 
Enter fullscreen mode Exit fullscreen mode

As you can see it's prefixed with "!", which allows us to use any command, not only git commands.

That's all for today, hope it helps in your developer journey. As always, feel free to share your thoughts and feedback in the comments. Till the next time!

Discussion (31)

Collapse
nerd profile image
Dhananjay Panage

As a beginner got to know a lot because of this, I would like to add one more to it git rm --cached filename it removes files from your repo but still is available locally.

Collapse
valeriavg profile image
Valeria Author

Thank you for the addition!

Collapse
vipinkrishna profile image
vipinkrishna

It removes files from from staging/index only

Collapse
safinghoghabori profile image
Safin Ghoghabori

I think it wont remove from remote repo but staging area.

Collapse
valeriavg profile image
Valeria Author

Git rm is the exact opposite of add.
So if you *add*ed files and then called git rm --cached smth it will remove that something from the staged changes. If you didn't change or add anything since the last commit, calling git rm will create this change (as if you would delete the file), staging it for your next commit.

Collapse
emptypockets profile image
Andrey Kondratyuk

Does it matter if branch changes are pushed to a remote before rebasing? Would the steps be any different?

Collapse
valeriavg profile image
Valeria Author

No, the steps would be the same, but force push would wipe remote branch commits that have not been pulled and may require hard reset on all local repositories, apart from your own.

Collapse
emptypockets profile image
Andrey Kondratyuk • Edited

I don't think I 100% follow.

If I created a branch git checkout -b my-new-branch and then stage, commit, and push:

  • git add .
  • git commit -m "my message"
  • git push origin my-new-branch

Does it matter that it the code was pushed to the remote? Could I then rebase as you described?

Thread Thread
valeriavg profile image
Valeria Author

Yes you can rebase my-new-branch from main with git rebase main locally, but you will need to git push --force my-new-branch to push these changes to a remote origin

Thread Thread
emptypockets profile image
Andrey Kondratyuk

Thanks for the clarification. I think I understand. My plan at the moment is to try it with different scenarios and make sure I really do get it.

Great guide. Very much appreciated!

Collapse
spha88 profile image
Siphamandla Mehlomakulu

Thank for this amazing article, could you please help me with one more issue.

How do I add changes made after a commit to the last commit. For example, I just created a nav menu and committed, after which I made a small change which I don't want to commit on its own but to add to the last commit.

Hope you understand and thank you in advance.

Collapse
valeriavg profile image
Valeria Author

Sure thing! Firstly, you add the changes with git add, then change the last commit with git commit --amend.
If you pushed your commit to the remote repository to push this change you'll need to use git push --force.

Collapse
myerschris78 profile image
Chris Myers

I tried git merge -X theirs hello.txt and received the message merge: hello.txt - not something we can merge Have you encountered this?

Collapse
valeriavg profile image
Valeria Author

Try git merge -X theirs dev instead.
I apologise for this mistake, git merge requires a branch name, of course, not the file name. I've corrected that in the article, thank you!

Collapse
myerschris78 profile image
Chris Myers

thanks!

Collapse
mayankpathak profile image
Mayank Pathak • Edited

Great Article @valeriavg , helpful for those, whose beginning with Git.

Collapse
joshuapatel profile image
Joshua Patel

How are you being compatible with GitHub by changing the default branch name? It's completely unnecessary. If you're using "master" as the default branch name, GitHub will still let you use it as the default branch name.

Collapse
valeriavg profile image
Valeria Author

When you create a repository on GitHub the default branch is main. It's not prohibited to use master as a default branch, but that would require a bit of a setup on GitHub to change the default name. Now I personally don't have a preference on how the branch is going to be called, but if a service, that I rely on a daily basis, asks me to do a one tiny naming change I prefer to comply out of respect.

Collapse
caa profile image
Dallascat

I got to the part of remote repository, made an example repository in github and tried the commands above and get a RSA key fingerprint "not known by any other names". I try to continue and get permission denied (publickey). Ideas?

Collapse
valeriavg profile image
Valeria Author

Try this solution from stack overflow: stackoverflow.com/questions/956089...

Collapse
bobbyiliev profile image
Bobby Iliev

Great article! Well done!

Here is also a free open-source eBook on how to get started with Git that might be helpful for some people:

GitHub logo bobbyiliev / introduction-to-git-and-github-ebook

Free Introduction to Git and GitHub eBook

πŸ’‘ Introduction to Git and GitHub

This is an open-source introduction to Git and GitHub guide that will help you learn the basics of version control and start using Git for your SysOps, DevOps, and Dev projects. No matter if you are a DevOps/SysOps engineer, developer, or just a Linux enthusiast, you can use Git to track your code changes and collaborate with other members of your team or open source maintainers.

The guide is suitable for anyone working as a developer, system administrator, or a DevOps engineer and wants to learn the basics of Git, GitHub and version control in general.

πŸš€ Download

To download a copy of the ebook use one of the following links:

πŸ“˜ Chapters

  • About the book
  • Introduction to Git
  • Version Control
  • Installing Git
  • Basic Shell Commands
  • Git Configuration
  • Introduction to GitHub
  • Initializing a Git project
  • Git Status
  • Git Add
  • Git…
Collapse
ebdonato profile image
Eduardo Donato

After read this, I am a git master now! 😎

Collapse
stuartcmd profile image
Stuart

Thanks for a marvelous Git tutorial.

Collapse
all_stacks_developer profile image
All Stacks Developer

Great article!

Collapse
swaranan profile image
Swaranan Singha Barman

Very useful

Collapse
chamsedinebouhouch profile image
BOUHOUCH chamseddine

thank you for sharing

Collapse
renas63 profile image
Renas

Very helpful article! Thanks for sharing

Collapse
johnjacobkenny profile image
Kenny John Jacob πŸ“˜

Great article, and I hope people will try all the commands out and spend more than 7 minutes in practicing these πŸ™‚

Collapse
bwildenhain profile image
Benedikt Wildenhain • Edited

I had to use "git config ––global init.defaultBranch main" to configure my git, the syntax using "=" isn't accepted with git 2.30.2.

Collapse
valeriavg profile image
Valeria Author

Thank you, I'll update the article to reflect this

Collapse
mehtab2899 profile image
Mehtab Multani

If you using latest version of git then use:
git config --global init.defaultBranch main