Git and GitHub: Getting started guide

In this article we will cover the basics for working with Git and GitHub: local and remote repositories, Personal Access Token authentication as well as collaborative work through branches and pull requests.

Git and GitHub

Git is a version control system and GitHub is a cloud Git service. GitHub has become a very popular source code repository because its plenty of features and has free access to public access projects. Git and GitHub are both huge tools and using them for the first time can be intimidating. This article aims to address the basic usage on a team scenario so future articles will address configuration and advanced scenarios.

Understanding the distributed repository usage in Git and GitHub

Using Git for source code version control involves working in both local and remote (centralized) repositories. Developers clone existing repositories or create their own local ones and then push their changes in remote repositories. One common (and natural for Git) practice is that developers control source code in different simultaneous versions (branches) for new features or bugs. Once they complete their job, they are mark the branch for review and integration (pull request).


Git and GitHub simplified workflow
Git and GitHub simplified workflow

Signing up to GitHub

You should open a free account in GitHub to follow this guide. Once on board, next step is to be sure you have one of the many Git clients out there. We won’t cover Git history and background here to keep this guide as light and useful as possible.

Git clients

The following is a non extensive list of available Git clients for several platforms. Depending on yours, you can opt for Git plugins for your favorite IDE, GUI clients and the standard command-line tool. We are going to use the latter to explain the mechanics and you can explore the best option for your development environment and team preferences.

Authentication in Git and GitHub

GitHub supports several authentication mechanisms (certificate, 2FA, among others). We will use the Personal Access Token (PAT) authentication in this guide. PAT is a safer replacement for standard username/password authentication and works almost in the same way with few improvements like custom expiry date and the ability to create several PAT each with its own fine grained permissions. To avoid entering your credentials after each command you should set up your username and e-mail and enable a credential helper store (this will allow you to save your different credentials):

git config --global demo40devweb
git config --global [email protected]
git config --global credential.helper 'store --file ~/.git-credentials'

The git credential command allows to save your credentials in the credential helper store. If you have configure other credential store types the command will replicate the credentials in each one. You can use the command below or enter the data interactively:

git credential approve << EOF


Managing sources in Git and GitHub

Creating a GitHub repository

Github’s simple UI shows the repository options. For our guide, we will create a new empty repository (demo in the example), push an initial version of the Demo application and then we will create a new branch in order to implement a new feature.

Creating a local repository from scratch

A common case for version control in GitHub occurs when you already have the source code and want to be controller on GitHub. For example, consider we have a demo folder with our sources (in this case a Spring Boot Initializr dummy project):

demo40dev@40dev:~/demo$ ls -l
total 32
-rw-r--r-- 1 demo40dev demo40dev   848 dic 20  2022
-rwxr-xr-x 1 demo40dev demo40dev 10284 dic 20  2022 mvnw
-rw-r--r-- 1 demo40dev demo40dev  6734 dic 20  2022 mvnw.cmd
-rw-r--r-- 1 demo40dev demo40dev  1225 dic 20  2022 pom.xml
drwxr-xr-x 4 demo40dev demo40dev  4096 dic 20  2022 src

In this case, some developer or the proprietary of the repository can upload or push the source code into the remote repository. In order to do so, we first create the local repository, commit the new sources in the local repository and then push changes to the remote repository.

The first Git command we are going to use is git init:

demo40dev@40dev:~/demo$ git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:   git config --global init.defaultBranch <name>
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:   git branch -m <name>
Initialized empty Git repository in /home/demo40dev/demo/.git/

The git init command tells Git that the current directory should track file changes (add, remove, new files not tracked before) which lead us to the next command git status:

shigeo@40dev:~/demo$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

nothing added to commit but untracked files present (use "git add" to track)

GitHub changed the dafault name for their first branch as main, so you could configure your git client to use main as the default branch name before creating a new repository:

git config --global init.defaultBranch main

Now, we add the demo project files to the local repository with git add . and commit changes. The dot indicates to add all non-managed source code and git commit -m "message" commits recently added sources to the local repository and add the message to the change set. In this case is “First commit”.

git add .
git commit -m "First commit"

You can see with git status the status of the source code changes in the repository.

demo40dev@40dev:~/demo$ git add .
demo40dev@40dev:~/demo$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   .gitignore
        new file:   .mvn/wrapper/maven-wrapper.jar
        new file:   .mvn/wrapper/
        new file:   mvnw
        new file:   mvnw.cmd
        new file:   pom.xml
        new file:   src/main/java/com/_40dev/demo/
        new file:   src/main/resources/
        new file:   src/test/java/com/_40dev/demo/

demo40dev@40dev:~/demo$ git commit -m "First commit"
[master (root-commit) b0cb492] First commit
 9 files changed, 607 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 .mvn/wrapper/maven-wrapper.jar
 create mode 100644 .mvn/wrapper/
 create mode 100755 mvnw
 create mode 100644 mvnw.cmd
 create mode 100644 pom.xml
 create mode 100644 src/main/java/com/_40dev/demo/
 create mode 100644 src/main/resources/
 create mode 100644 src/test/java/com/_40dev/demo/
demo40dev@40dev:~/demo$ git status
On branch master
nothing to commit, working tree clean

Pushing code to a remote GitHub repository

Now that we have our local repository, we will connect both local and remote repository. For our example the command to connect them is git remote add <name> <url>:

git remote add origin

When all changes in local repository are committed, next step is to synchronize changes with the remote repository in GitHub. This is done with the git push command:

demo40dev@40dev:~/demo$ git push -v origin master
Pushing to
Username for '': 40devweb
Password for 'https://[email protected]': 
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 274 bytes | 274.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
POST git-receive-pack (449 bytes)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
   b0cb492..f885213  master -> master
updating local tracking ref 'refs/remotes/origin/master'

Collaborate in GitHub

Adding users to your remote repository

You can add new users and collaborators other than the repository owner from the Github project page.

Adding a collaborator to a private repository

The command git clone will clone a remote repository in a local repository (current folder by default). In this case, user shnishi should have permissions on the remote demo40devweb/demo repository and has configured credentials as showed in previous sections:

shnishi@40dev:~$ git clone
Cloning into 'demo'...
remote: Enumerating objects: 34, done.
remote: Counting objects: 100% (34/34), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 34 (delta 4), reused 32 (delta 2), pack-reused 0
Receiving objects: 100% (34/34), 59.48 KiB | 441.00 KiB/s, done.
Resolving deltas: 100% (4/4), done.

Creating a branch

When working with several developers on the same code base, it is expected that you or your development team derive a new version (branch) of the official repository while doing changes and merge this version with the main version once it is ready. Branching strategies may vary according to projects and organizations and we will discuss them in a future post for the sake of simplicity. For the example we will create a new branch for feature XYZ and modify some files:

shnishi@40dev:~/demo$ git branch 
* master
shnishi@40dev:~/demo$ git branch featureXYZ
shnishi@40dev:~/demo$ git checkout featureXYZ
Switched to branch 'featureXYZ'
shnishi@40dev:~/demo$ git branch 
* featureXYZ

Let’s review the commands issued:

  • git branch: Shows the branches created in the local repository
  • git branch <name>: Creates a new branch with the specified name
  • git checkout <name>: Changes the current working files

Now we will proceed modifying some files and pushing changes to GitHub. Please note that git push <remote> <localBranch> will create the local branch in the remote repository and will make it visible to other team members.

shnishi@40dev:~/demo$ echo "New modification for featureXYZ" > featureXYZ.txt
shnishi@40dev:~/demo$ git add .
shnishi@40dev:~/demo$ git commit -m "Added feature XYZ" 
[featureXYZ 10fca79] Added feature XYZ
 1 file changed, 1 insertion(+)
 create mode 100644 featureXYZ.txt
shnishi@40dev:~/demo$ git push origin featureXYZ
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 312 bytes | 312.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote: Create a pull request for 'featureXYZ' on GitHub by visiting:
 * [new branch]      featureXYZ -> featureXYZ
GitHub showing featureXYZ branch

Creating a pull request

Once the feature XYZ is complete we are ready to merge the featureXYZ branch with the main branch in order to release a new version of our demo application. You could do that with git merge <featureBranch> but al alternative is to create a request to a project leader or senior developer to review the modifications and approve the marge with official branches (like QA or UAT environments branches). This is what is called a pull request and depending on the permissions model on the repository may be the standard (and only) mechanism to introduce new features to repositories. A pull request can be created from the GitHub repository in the Pull requests tab and selecting the branch we want to be pulled into another branch and selecting Create pull request.

Creating a pull request in GitHub

Once created, the specified reviewers receive a notification for the review of the pull request and proceed to accept (merge) the proposed feature or they can reject it. The entire workflow is fully configurable. In this example the branch featureXYZ is no longer needed so we proceed to deleted in both local and remote repositories:

shnishi@40dev:~/demo$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
shnishi@40dev:~/demo$ git branch -D featureXYZ
Deleted branch featureXYZ (was 10fca79).
shnishi@40dev:~/demo$ git push origin --delete featureXYZ
 - [deleted]         featureXYZ

Merging remote changes into local repositories

It is possible that other developers modify source code in the same branch but in the remote repository. In that case you would need to synchronize your local repository with the most recent changes. The git pull command updates your local repository with the remote changes. For our example, git pull updates the local repository with the remote changes (the feature XYZ branch merge).

shnishi@40dev:~/demo$ ls
mvnw  mvnw.cmd  pom.xml  src
shnishi@40dev:~/demo$ git pull 
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (4/4), 905 bytes | 905.00 KiB/s, done.
   cb466f0..91e7f48  master     -> origin/master
Updating cb466f0..91e7f48
 featureXYZ.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 featureXYZ.txt
shnishi@40dev:~/demo$ ls
featureXYZ.txt  mvnw  mvnw.cmd  pom.xml  src

Advanced topics

This article covers the basics for source code control with Git and GitHub. Future articles will cover:

  • GitHub Actions: CI/CD functionalities
  • Projects and Issues for project work item management
  • Security and workflow management
  • Codespaces: Container development environments