I have been using Git for a number of years and I can remember feeling quite daunted at the complexity of some of the commands I saw on the internet. When I started using Git on a daily basis I soon realised that the basics were quite simple and the complexity only lay further down the road with commands like cherry-pick or rebase.
Whilst Git does sometimes make me scratch my head, it is never as bad as the days of SVN where I would have a notepad of 'fix' commands that I would copy and paste into my terminal to solve random problems. I usually didn't even know what the problems were, just that this command fixed things so I could continue on and not lose work.
I have been meaning to write a Git article for a while, but I felt that I needed to know a lot more about the system before I could do that. I feel like I'm in a position to talk about the basics now so in this article I will lay out the bare minimum commands you need to get started with Git. I have grouped commands together here where it makes sense to do so.
git init/git clone
As a starting point to using Git you'll need to get a repository up and running. You can either create your own repository using init or download a remote repository using clone.
To create a Git repository on your local machine navigate to an empty directory and type the git init command.
$ git init
Initialised empty Git repository in /gittest/.git/
This command will perform two actions.
- Create a directory called .git. This is used by Git to keep track of your local repository. Any changes you make to the files or structure of your repository will be tracked in here. If you delete this directory you will essentially be removing Git from the project. This is a good way of "resetting" a local Git repo if you get completely stuck.
- With the repository in place, Git will automatically create a default branch. This used to be called 'master' but recent changes in Git mean this is now called 'main'.
As a standard practice you should be using git init on your local projects to create a git repo. If nothing comes from that project then it doesn't matter, the world won't have noticed. If it does and you want to move it to somewhere like GitHub then all you need to do is add a remote and push the code upstream. Most Git hosting sites will have some instructions on adding existing repos to the site so it's just a case of following a few simple instructions.
If you create a new repository on GitHub (or want to download an existing repository) then you need to use the command git clone along with a reference to the repository you want to copy locally. This will create a directory and download the remote repository.
$ git clone https://github.com/philipnorton42/PHP-Fractals
Cloning into 'PHP-Fractals'...
remote: Enumerating objects: 194, done.
remote: Total 194 (delta 0), reused 0 (delta 0), pack-reused 194
Receiving objects: 100% (194/194), 57.59 KiB | 1.64 MiB/s, done.
Resolving deltas: 100% (94/94), done.
Once complete you will have an exact copy of the remote repository on your local machine. I should note that there are essentially two types of authentication used to access Git repos, namely https (as seen above) and ssh. The ssh type access looks a little different, but has the same functionality in the end.
$ git clone [email protected]:philipnorton42/PHP-Fractals.git
If you are using GitHub, GitLab or any third party git service, then you are generally given https access to the repository if you don't have access to write back to it. If you have all of the necessary access writes to the repo then you will probably see ssh access.
git status
By far my most commonly used command is git status. This gives you an overview of the changes you have made to your files. If nothing has changed in your files then you should see something like this.
$ git status
On branch main
Your branch is up-to-date with 'origin/main'.
nothing to commit, working tree clean
If you make a change to any of the files in the repo then running the status command again will show you the files you changed.
$ git status
On branch main
Your branch is up-to-date with 'origin/main'.
Changes 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: src/Generator/Mandlebrot.php
no changes added to commit (use "git add" and/or "git commit -a")
If you are in any doubt about the state of your repo then running git status will show you. It will also show you some commands that you can use to perform actions on those files. Let's move on to look at some of those commands.
git add/git commit
Once you have made a change to your repository and can see those changes in your status output then you'll need to tell Git about those changes. This is where the git add and git commit commands come in. To add a file you just run git add and the filename, there is no output, but you can run git status again to view the change.
$ git add src/Generator/Mandlebrot.php
The add command can use wildcards, so you can use the star (*) operator to add one or more files in a directory, or everything in the repo.
$ git add src/Generator/*.php
You can also use the dot (.) operator to add everything in a directory, or everything in the repo.
$ git add src/Generator/.
The commit command will obviously write the change to the repo, but there is something that needs to be understood about the state of files in Git. When you ask Git for the state of the files you'll see something about a stage. There are essentially three different states for a file in a git repo, and understanding the difference between them is important.
- Normal - A file that hasn't been changed.
- Changed - A file that has been changed in some way.
- Staged - A file that has been changed and moved into the stage ready to be committed.
When you add a file you are moving it from the changed files into the stage. By running git status you can see the files you have in the stage.
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/Generator/Mandlebrot.php
You can now run git commit to write those change to your git repo. You should always create a commit with a commit message, so if you use git commit without any other flags you will see a screen asking you to fill in some detail about the commit. This extra step can be skipped by using the -m flag and appending a message. When you run the command you will see your commit message as well as a breakdown of what you changed in terms of files changed and number of lines added and removed.
$ git commit -m "Changed stuff."
[main 7796d52] Changed stuff.
1 file changed, 3 insertions(+)
Adding some files and not others to the stage means that you can commit parts of the changes to your repo, rather than everything at once. This allows you to make lots of changes across your codebase but then commit only parts of that change one at a time.
I have always found it useful to think of a commit as the standard unit of measurement in a Git repo. Everything else is just ways of navigating around those commits.
git restore
There is no output for this command, but once run it will restore a file to a previous state. For example, to restore a file to a previous state and throw away all the changes just run git restore like this.
$ git restore src/Generator/Mandlebrot.php
If the file is in the stage then this command will have no effect. You will need to add the --staged flag to do this.
git restore --staged src/Generator/Mandlebrot.php
This won't reset the file, it will just move it from the stage to your changed files again. This allows you to back out of a commit without losing the changes you have made to a file.
git push/git pull
If you are working on a remote repository then you will need to understand the push and pull commands. After you have made a change to your local repository you will need to push those changes upstream using the git push command.
Although these commands can be run on their own, it is considered best practice to run them with the remote name and the branch (or tag) you are acting on. By default the first remote repository created is called "origin", so to push the "main" branch to the remote repository you would use the following.
git push origin main
The full output should include what happened during the push, including what branch was updated.
$ git push origin main
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 540 bytes | 540.00 KiB/s, done.
Total 4 (delta 3), reused 0 (delta 0)
To https://github.com:philipnorton42/PHP-Fractals.git
b303ddbe4..c8d09d200 main -> main
Conversely, if you need to pull down changes from a remove repository then use the git pull command. If you have no changes then this will produce an "Already up to date." notice.
$ git pull origin main
From https://github.com/philipnorton42/PHP-Fractals
* branch main -> FETCH_HEAD
Already up to date.
The push and pull commands normally run without any issues. The main problem you'll probably encounter is if you have local changes and you try to pull changes to the same files from the remote branch. Git will complain that you can't do that with an error stating "The following untracked working tree files would be overwritten by merge" and a list of files. All this means is that you need to either restore or commit your local files before you can push.
There is also a similar issue you will likely encounter with the push command. If you try to push to a remote repository that someone else has pushed to then Git will complain. The problem is that the head commit your local branch is in a different state to the remote and what Git wants you to do is pull locally to reconcile the differences. All you need to do is pull the remote changes down and you can push your changes back up.
git branch/git merge/git checkout
In Git, a branch is more or less what it sounds like, a way of separating your current work stream so that it is not part of the "main" branch. To create a branch in Git you use the git branch command.
$ git branch new-feature
Note that this doesn't actually mean that you are now working on the new branch, for that you need to use the git checkout command.
$ git checkout new-feature
Switched to branch 'new-feature'
It is possible to perform these two actions in one go using the -b flag on the git checkout command.
$ git checkout -b new-feature
Switched to a new branch 'new-feature'
Branching in Git is easy to do and does not bloat the repository. This is why branching in Git is usually referred to as 'cheap'.
Once you have finished working on the other branch you can merge it into you main branch using the git merge command. The merge command works by merging the named branch into the branch you current have checked out. This means that if you want to merge a branch into the main branch you need to first checkout the main branch and then merge the other branch into it.
$ git merge new-feature
Updating e4499f9..363c8ed
Fast-forward
src/Generator/Mandlebrot.php | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
This looks like the output of a commit, and really, it is. By merging branches in this way we are creating a special kind of commit called a merge commit.
Problems you encounter with git merge will be when Git doesn't know how to merge code changes from one branch to another. If this happens you will get a merge conflict and Git will put the merge into a temporary state. It's then up to you to hand edit the files to fix the conflict. When you open the files you'll see something like this.
// Loop through the items.
<<<<<<< HEAD
foreach ($array as $item) {
=======
for ($i = 0; $i < count($head) -1; ++$i) {
>>>>>>> dev-branch
All you need to do is look at the conflict markers as these show you what changed in what branch (the conflict markers are the <<<<<<<, =======, >>>>>>> strings in the example above). This will show you what is in the current branch (in between the <<<<<<< and the =======) and what is in the other branch (in between the ======= and the >>>>>>>). Solving merge conflicts can be a difficult task, especially on large projects, but you just need to ensure there are no conflict markers left. There are plenty of tools exist to help you through this so merging can be made easier. I tend to use the merging tools in PHPStorm as they are clear and easy to use and allow you to compare and contrast the file changes in a visible manner.
When you are finished you just run git commit to finalise the changes in the merge.
git log/git diff/git show
If you have made changes to your repository then you'll probably want to know more about what has changed. The git diff command will show you all of the changes that have been made to files. The output of the diff command will show you things that have been added with a "+" and things that have been removed with a "-". Taking an example of a recent change to the Drupal CMS we can see this in effect here.
diff --git a/core/.cspell.json b/core/.cspell.json
index 7cfaab0701f862662d68bf9c5c4a0184cba5d92f..e1c830006163d8c37680b714fd6daca7766ad959 100644
--- a/core/.cspell.json
+++ b/core/.cspell.json
@@ -34,7 +34,8 @@
"ignoreRegExpList": [
"^msgstr .*",
"!!binary .*",
- "%[0-9][0-9A-F]"
+ "%[0-9][0-9A-F]",
+ "\\Wi18n"
],
"dictionaries": ["drupal","companies", "fonts", "html", "php", "softwareTerms"],
"dictionaryDefinitions": [
Breaking down this file a little we can see that the file core/.cspell.json file has been changed. This section towards the head of the code changes shows that the change happens around line 34 and that one line was changed and one line was added. The format of the diff output is useful to understand as you tend to see it a fair amount in Git.
Once you have spent some time making changes to your project it is probably time to start looking at those changes. This is done using the command git log. On its own the command will show you a full list of the last commits made to the branch you currently have checked out.
commit dfadd0ecb536d047a60544af67492ddf8e9a6d43 (origin/main, origin/HEAD)
Author: Philip Norton <>
Date: Sun Dec 24 15:54:19 2017 +0000
added a test file that zooms in on a mandlebrot set
commit 0400c4efa6e610f587e59c2c647f308005d8875f
Author: Philip Norton <>
Date: Sun Dec 24 15:53:14 2017 +0000
fiddling with some of the test files
commit 16fdb62bc5721ff2744d2524c59e9f54d757a45e
Author: Philip Norton <>
Date: Fri Dec 22 22:29:13 2017 +0000
removed a blank line from the Palette class
There are many flags that can be applied to the git log command. I could write an entire article on just the flags that can be applied here. However, one variant of the git log command that comes in useful is this one that shows
$ git log --oneline --decorate --graph --all
You may have noticed the long strings of letters and numbers in some of the examples in this section and above. These are called the commit sha (Simple Hashing Algorithm) and is a way for Git to uniquely identify each commit in the repository. These sha identifiers are useful when you want to look at or take action on a specific commit. To look at the changes made in a commit use the git show command.
$ git show 16fdb62bc5721ff2744d2524c59e9f54d757a45e
This will show you the same as the diff command, but it will be encapsulated into a single commit. You can also use the git diff command to show the differences between the current state of the project and when a commit was made using the hash value.
What's Next?
What I have detailed above is a very basic look at the Git commands I use the most. You should spend time getting familiar with some of the flags that are available for these commands. More than this though, you should be familiar with some of the inner workings of Git and how it ties things together. I have always thought of a Git repository as a collection of commits, with each commit linking to the next commit (or commits) in the history of the project. I'm not sure if this is a good analogy, but it has helped me solve some sticky code merging problems in the past. Thinking in terms of commits helped me to figure out what is being merged from one branch to another and what to do about merge conflicts.
Also important to look into is the concept of local vs remote when it comes to interacting with Git repositories. This is essential if you are working with other people on a project and want to check out their branch and take a look.
Once you have mastered these basics then there is plenty of other commands to start looking at. If I think about how often I use commands outside of those detailed above then I suppose commands like clean, reset, tag, stash, cherry-pick, or rebase would be the next things to look at. Be warned though that the rebase command in Git can get messy so you should approach it with caution, especially if you are working on a repository with other developers as you can make life difficult for them.
The most important piece of advice I can give is just to get stuck in and start committing, you will soon get used to the commands and how to work around problems. You don't need to push a repo remotely, but services like GitHib, GitLab or Bitbucket exist that allow free and simple access so you can start experimenting with remote repositories. There are also a few GUI tools like SourceTree or GitKraken that will help you visualise the changes being made to your repository.
Add new comment