A Git Practice | To Be a Master in the Github and the Git

Series: Git and GitHub

A Git Practice | To Be a Master in the Github and the Git

by Kim Estoesta for GitHub

0. Before We Start

We assume that you know nothing about Git and GitHub. But we do assume that you know some sorts of the basic command lines. First of all, you have to register a GitHub account.

So in order to create a remote repository, we have to go to github.com and if you haven’t got a GitHub account, please sign up an account by going to the signing webpage github.com/join.

Then we have to create an ssh connection from our personal computer to the GitHub so that we don’t have to type in the password every time we push. You can refer to this document about how to create an ssh-key and then add it to the ssh-agent.

Then in the terminal, we set our user name and the user email to global so that GitHub can know who is pushing (we are going to explain git push later).

$ git config --global user.name <username>
$ git config --global user.email <primary email address>

Note that we have to replace the <username> to our GitHub username and the <primary email address> to out primary email address in the GitHub settings.

  1. Basic Setups

First of all, let’s create a folder called GitPractice in the home directory.

$ mkdir GitPractice

To go into this folder by,

$ cd GitPractice/

Add a file called HelloGit.txt by,

$ touch HelloGit.txt

Write Hello Git! into this file,

$ echo 'Hello Git!' > HelloGit.txt

Check out the result

$ cat HelloGit.txt

Check the git status of this folder

$ git status

If you are not doing anything wrong, you are able to get,

fatal: not a git repository (or any of the parent directories): .git

Initialize this folder to a local git repository

$ git init

Now, it is going to create a .git file in our folder. We can check this by,

$ ls -a

Check out the git status again,

$ git status

this will give us,

On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
          HelloGit.txt
nothing added to commit but untracked files present (use "git add" to track)

this is telling us the HelloGit.txt file is not added and committed to the local git repository. Then we do,

$ git add HelloGit.txt

Check out the git status once again,

$ git status

then we can find out that the git status of this file actually changes, with

On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
       new file:   HelloGit.txt

Then we are able to commit this file by,

$ git commit -m "Update HelloGit"

So far, we have already created our local repository.

2. Go Remote

If we want to push our local repository to remote now, we will get an error,

$ git push -u origin main

Output is,

error: failed to push some refs to 'origin'

This is because we haven’t linked any remote repo URL to this and the git can not know where to push. We can verify this reason by,

$ git remote

This will give us nothing back.

On the GitHub home page, do one of the following:

  • In Your repositories, choose New repository.
  • On the navigation bar, choose to Create new (+), and then choose New repository.

Type in the Repository name,

GitPractice

Don’t select anything else, just click the green button “Create repository”. Then we are able to get a quick setting-up instructions for the repo.

Look at these instructions,

…or push an existing repository from the command line

$ git remote add origin git@github.com:<username>/GitPractice.git
$ git branch -M main
$ git push -u origin main

Ok, so now let’s go back to our command line.

First of all, we type in (remember to change the <username> to your own),

$ git remote add origin git@github.com:<username>/GitPractice.git

then we go to the main branch,

$ git branch -M main

finally, we can push our result to the GitHub

$ git push -u origin main

After this, we can then refresh the GitHub webpage of the GitPractice repo, so then we are able to find a HelloGit.txt in this remote repository.

3. Pull Back

Now we have two repositories, one is a local repo (on your PC) and another is a remote repo (on the cloud server of Github). We can directly change the remote repo through the User Interface of the GitHub.

To do this, we then go to our Github repo webpage, then we wanna create a README.md file to help people understand what we are doing right now. Select Add file > Create a new file to create a markdown file.

Type in the file name as Readme.md, and write in the file with,

# This is a git practice repo.

Then we go to the bottom and find the commit part. We type in the commit message as,

Create Readme.md

Then select Commit new file, then we are able to see the newly created file in our remote repo. Now let’s go back to our local repo.

$ ls

The output should be,

HelloGit.txt

This means that the local is not automatically changed when the remote repo changed. So what we have to do now is to pull the files from the remote repo back to the local repo by,

$ git pull origin main

then we check out the files again by,

$ ls

Now we are able to find a new Readme.md file that is exactly the same as our remote repo.

HelloGit.txt Readme.md

4. Managing Branches

In our local repo, now we would like to create two new branches, one is called testing, the other is called checking,

$ git branch testing
$ git branch checking

We can then use the git branch to see the change,

$ git branch

we will get,

  checking
* main
testing

by this result, we know that the git branch command only creates new branches, but not actually switch to the newly created branch. To switch the branch to the testing branch, that is,

$ git checkout testing

we will get,

  checking
main
* testing

Now let’s see our remote repo, and there is still one main branch. In order to add these two new branches to the remote repo, we go back to our local repo and type in,

$ git push origin testing

This will push the testing branch in the local repo to the remote repo. Similarly, we can also do this to the checking branch,

$ git checkout checking

then,

$ git push origin checking

refresh our remote repo webpage, we can find that now we have three branches here.

So now we are in the checking branch and we want to do something here. We add a file called CheckList.txt by,

$ echo 'Things to check here...' > CheckList.txt

check out this file by,

$ cat CheckList.txt

in the checking branch, we can then add and commit this file and then push it to the remote checking branch,

$ git add CheckList.txt

then,

$ git commit -m "Add CheckList"

then,

$ git push origin checking

we can check the result by,

$ ls

we will have,

CheckList.txt HelloGit.txt Readme.md

If we switch to other branches, for example, the testing branch by

$ git checkout testing

then check out the files

$ ls

we will have,

HelloGit.txt Readme.md

we can have the same result in the remote repo.

Then if we want to merge the checking branch with the main branch, what should we do? First, we have to go back to the main branch,

$ git checkout main

secondly, we are able to merge these two branches by,

$ git merge checking

finally, we have to use git push to push the result to the remote repo,

$ git push origin main

By these means, we are able to merge the checking branch back to the main branch. But now it’s been a problem because the testing branch is still not being updated. It is a good idea if we use a pull request to pull the files from the main branch to testing. To do this, we have to go back to the testing branch,

$ git checkout testing

in this branch, we use the git pull command to pull from main,

$ git pull origin main

after that, we are successfully pulling the files from main to testing locally, then we have to make it happens for the remote repo,

$ git push origin testing

So far, all the branches are on the same version. Congratulations!

5. Resolve Conflicts

In the testing branch, suppose we modify the CheckList.txt file by,

$ echo 'Successfully Checked!' > CheckList.txt

also we add, commit, and push it to the remote repo,

$ git add --all
$ git commit -m 'Successfully Checked!'
$ git push origin testing

Then we switch to the main branch, also, we modify the CheckList.txt file by,

echo 'Failed' > CheckList.txt

Similarly, we add, commit, and push it to the remote repo,

$ git add --all
$ git commit -m 'Failed'
$ git push origin main

In the main branch, if we want to merge with the testing branch, we are going to cause a merge conflict.

$ git merge testing

We will get,

Auto-merging CheckList.txt
CONFLICT (content): Merge conflict in CheckList.txt
Automatic merge failed; fix conflicts and then commit the result.

By git status, we can see that there’s actually a dual modify in CheckList.txt.

$ git status

we will have,

On branch main
Your branch is up to date with 'origin/main'.

You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)

Unmerged paths:
(use "git add <file>..." to mark resolution)

both modified: CheckList.txt

no changes added to commit (use "git add" and/or "git commit -a")

so we then open the path,

$ open ./

then we choose an editor to open the CheckList.txt file, we are going the Sublime here. After opening it, we can see that,

<<<<<<< HEAD
Failed
=======
Successfully Checked!
>>>>>>> testing

To resolve this conflict, we have to delete:

<<<<<<< HEAD
=======
>>>>>>> testing

and choose which one to keep. In our case, we are going to keep the successful one.

Successfully Checked!

Save and close the file.

Finally, we have to add this file to both the local repo and the remote repo,

$ git add .
$ git commit -m 'Resolve Conflict By Using Successful'
$ git push origin main

In the end, we pull this to update the other two branches,

$ git checkout testing
$ git pull origin main
$ git push origin testing
$ git checkout checking
$ git pull origin main
$ git push origin checking

6. Version Control

Now we go back to the main branch and make a new file called account,

$ git checkout main
$ touch account

then we use nano to edit this file (vim and emacs are also okay).

$ nano account

In this file, we type in,

Username: Whoami
Password: 0ef186ac70e04ea33b4c1853d2526fa2

then we press ^o, ^x to write and close nano.

After this, we do git push process to the remote with the previous steps,

$ git add .
$ git commit -m "add account"
$ git push origin main

then we edit this file by,

$ nano account

in this file, we rewrite this by,

Username: Whoami
Password: ******************************

then we press ^o, ^x to write and close nano. Also, we can do the git push process to the remote with the previous steps,

$ git add .
$ git commit -m "add account"
$ git push origin main

So now, suppose we forget the password (that is barely impossible to remember), what can we do now? We have already changed the account file and the password is all *****s now. What we want to do is to go back to the previous versions of this file so that we can retrieve the password.

First of all, we can check all the commits by,

$ git log

we can also show all the changes through the -p option,

$ git log -p

but sometimes, this can be annoying because we don’t want to show all the infos, then we can use the — pretty=oneline option,

$ git log --pretty=oneline

In the git, the HEAD file in the .git folder gives us the information of the current commit, and we can use the following command to show the hash of the current commit,

$ git reset --hard HEAD

the output of this command will be the value of the hash current commit,

HEAD is now at <ShortHash> modify

So how can we go back to the previous version? We can use HEAD^ to show that we would like to transfer to the last version,

$ git reset --hard HEAD^

it is clear to see that the hash value of the current commit changes after running this command, now let’s check our account file by,

$ cat account

we can then have,

Username: Whoami
Password: 0ef186ac70e04ea33b4c1853d2526fa2

this shows that we have successfully go back to the last version and we can retrieve our password. Before this command, we have,

HEAD  ---> c784464 (HEAD -> main, origin/main) modify
97d029c add account
3214178 Resolve Conflict By Using Successful
...

after that, we have

HEAD -┐    c784464 (HEAD -> main, origin/main) modify
└--> 97d029c add account
3214178 Resolve Conflict By Using Successful
...

So what if we want to push this change to remote? Let’s try,

$ git push origin main

Oops, we have an error,

error: failed to push some refs to 'git@github.com'

The reason why we have this error is that the remote repo is treated by git as a future version and the local treated by git as a past version. The git only allows us to push from future to past or to pull from past to future, but not vice versa. To force a push, we have to use the -f option,

$ git push -f origin main

This will force the remote repo to go back to the last version. However, this can be a disaster if we mistakenly push the pervious version to remote because we can hardly get this version back (we indeed can get it back, but why don’t we use a more clear way?). Let’s try to get it back now. We can see the history of our git commands by,

$ git reflog

in the output, we find out the future one that we move to the HEAD (instead of HEAD^). Copy its hash value, for example, in this case, it is c784464,

c784464 HEAD@{1}: reset: moving to c784464

then we use the reset command to go to the future,

$ git reset --hard c784464

We don’t have to add anything because this commit (modify) will automatically being added to our git stage. What we have to do now is to push it directly,

$ git push origin main

Now it is clear to see we get our version back both locally and remotely.

However, sometimes, we don’t want to go back because when we do more things, it seems that we can hardly find out our abandoned versions. What if we create a new commit of the past version without getting rid of the current version?

First, we revert to the last version (by using current commit hash),

$ git revert c784464

This will automatically create a new commit to the future. We can use git log and git status to check this,

$ git log --pretty=oneline
$ git status

Finally, we push this to the remote by,

$ git push origin main

The difference between revert and reset is that, before the revert command, we have,

HEAD  ---> c784464 (HEAD -> main, origin/main) modify
97d029c add account
3214178 Resolve Conflict By Using Successful
...

After the command, we then have,

      ┌--> bc7c6e4 (HEAD -> main) Revert "modify"
HEAD -┘ c784464 (HEAD -> main, origin/main) modify
97d029c add account
3214178 Resolve Conflict By Using Successful
...

7. Add tags

Suppose we have a new publish v0.1, how can we link a tag named v1.0 to this commit? Firstly, let’s create a file named PUBv1.0.

$ touch PUBv1.0

then we add, commit, and push this file to the remote repo,

$ git add .
$ git commit -m "release v1.0"
$ git push origin main

Now, we can add a tag to this commit by git tag command, this will add a tag to the latest commit,

$ git tag v1.0

then we use git tag to check the tags,

$ git tag

we can also use git show to show this commit,

$ git show v1.0

This is the same as we directly use git show to the short hash of this commit,

$ git show 1332a2b

The tag will not automatically be pushed to the remote repo, we have to use git in order to push this tag,

$ git push origin v1.0

If we want to delete a tag, we can use -d option to delete it locally,

$ git tag -d v1.0

Then we use the push command to delete it remotely.

$ git push origin :refs/tags/v1.0