😁Git and Github

All you need to know about Git and Github

GIT

Gotchas

How to remove .DS folder in mac

When we first initialize git, using git init, our working folder turns into a working directory where .git folder is created. Not only this, but when you use Mac, for some reason, .DS folder gets created. You can do git status and you see this folder. We can remove this as shown below.

Removing .DS folder (I've upvoted for this)

Basics

  • A general idea -> We have 3 areas

General Diagram
  • Areas

    • Working Area (Not staged area) -> When ever any files are changed/modified

    • Staging Area -> We can add working area files to staging area using git add -A command

    • Committed Aread -> We can add all the files in staging area into committed area as a commit.

  • A folder can be turned into a working directory by having .git file in it. This can be done by using command git init

  • Whenever we modify any file, that file will become untracked file. Meaning

Untracked files

When file initially gets created and before adding it to staging area, the file/files will be untracked

Staging area

staging area

Git status

git status gives the following information

  • branch info

  • Whether or what files are in working area (not yet in staging area) Or files in staging area (not yet committed)

git status (files are in working area)
git status (files are in staging area)

Git Log

git log gives the details of all the commits:

  • Commit ID (unique Id for each commit)

  • Head commit pointer (tells which commit is the head)

  • Author of the commit

  • Date of commit

  • Commit messge

git log
git log command

Branches and Commits

  • Branch is a unique set of code changes that holds commits

  • Default branch is master

branch
  • If we want to add some features, we might not want to spoil the code of the existing branch by adding unwanted commits. Adding unwanted commits and then reverting back will be a big headache. In some cases we might not have access to add commits to the master branch (for example open source projects). In such case we better create a replica of the master or some other branch we would like to add our commits to. This replica is called a sub-branch or feature branch which we can name anything. To this branch, we start adding commits and then once our code works properly we can merge it back to master branch/ to branch which we branched out from.

  • This process is called merging branches

Landing page branch branched out from master

Check current branch

The number of branches in our project and the current branch we are working with can be checked using

git branch
we have a single branch at the moment and that's the current branch

Create new branches

First Way

  • To create a new branch from master and still be in the master branch

// currently in master branch

git branch second-branch

To switch to second-branch

git checkout second-branch
switch branch (method 1) - old way

Second Way

In the above process, we first create a branch with git branch second-branch and then switch to that branch with git checkout second-branch. But we can do both of these Creating and Swtiching in one single command.

// Be in the branch you would like to branch out from, like in above case

git branch -b second-branch

Note : The above command can create two branches with same name, so I suggest you to run this command if the branch with that name doesn't exist

Third Way (Git version 2.23+)

The word checkout can be confusing so git introduced new word switch to switch branches and create new branches and then switch to them.

// To switch to some other existing branch

git switch branch-name

// To create a new branch and then swtich to that branch

git switch -c branch-name

// the flag -c refres to create
git switch to existing branch
git switch to a new branch (first create and then switch)

Merge Branches

Based on above commands we now know how to create a branch from one branch. Let's say we did that and added some changes to the second-branch. And now, we need to merge the second-branch into the master branch.

Let's say I need to merge second branch into master branch. The source is second-branch and the destination is master branch. I need to be in the destination to receive the source.

Meaning, I need to be located in the master branch inroder to merge second-branch into master

merge from second-branch to master
// Merge second-branch into master

git merge second-branch

What is Head

Head

Head is the latest commit of a particular branch. Head points to the latest commit in a branch. That's the reason when we switch from one branch to other branch the head might change as the switched branch would have the latest commit as shown in the fig above.

In the below example, we created two branches from master

  • branchOne

  • branchTwo

We added two commits to branch two and one commit to branch one. Now, when we log, each branch shows different head as the heads are pointing to the latest commits of that particular branch.

branchOne
branchTwo

Checking out commits (Detached Head)

If we checkout branches, that means we are switching from one branch to another as explained above which inturn means we are pointing to the latest commit of that particular branch.

But, can we checkout a particular commit? A commit could belong to multiple branches.

Yes, we can checkout a particular commit. Checking out a particular commit is called Detached Head. We are detaching a commit and our head points to that particular commit, hence it's called detached head.

When the head points to particular commit then this means that head is not pointing to latest commit of any particular branch. The head is pointing to a commit that can be present in multiple branches. We can do lot of things detached heads like

  • Changing content of that commit and adding back to a particular branch

  • Many more which we will explore later

Let's look at 3 stages.

  • Before checking out the commit

  • Checking out the commit

  • Switching back or checking out back to master

  1. Before Checking out the commit

current branch is master

We are in the master branch. Let's see some of the commits in the master branch and then checkout a commit

git log

2. Checking out the commit

Checking out this commit is done by command

git checkout 3dc84fccd7dd31aab7f287794112a34e17939502

Now we have the head pointing out to this commit and not any branch's latest commit. That is why we have detached head.

Detached head

Now, on doing git branch we get this commit (head current pointer) listed as a branch, but this is actually not a branch as we know.

Detached head is not a branch even though it shows like that

Detached Head Vs Branch

Considering above explanation we know detached head is some random commit that might belong to one or more branches but that itself is not a branch. So what's the difference between a detached head and branch and why we need detatched head.

The first difference is "Detached head" is a commit that doesn't belong to a particular branch so it is not a part of branch. Where as branch is a commit that is linked to a particular branch.

When we switch back to other branch, the detached head dissappears if we don't merge it into a branch.

3. Switching back to master

switching back to master
Making changes on detached head and adding commits to detached head and merging into master
Git log after meging the detached head (made as branch) into master branch

Deleting And Restoring Data

Let's see how to delete

  • Working directory files (part of previous commit)

  • Unstage changes

  • Staged changes

  • Latest commits

  • Branches

How staging area handles the files

Before we understand how we delete files let's see how staging area handles the files.

// Files in staging area

git ls-files

The above command gives info about all files present in staging area. Meaning, everything that was a part of previous commit.

Delete Working directory (committed) files

Let's now say I delete a file from working directory (remember all the files were previously committed and all are in staging area now). Having said that, even though I delete a file from working directory, it would still be present in staging area.

staging area files

For next commit, we need to remove this file. For that we first need to delete this file from staging area. So once we delete the file from working directory like shown above, we need to add the changes of file removal in staging area like below.

staging area removing file

Undo or Restore unstaged changes

Tracked file - The file that is already a part of staging area. (add command used atleast once on this file)

Untracked file - The file that is added newly but never added to staging area with add command

Let's say we have committed everything and now our staging area files is equal to committed files. Let's now add some changes to an existing file.

After some time, now, we want to get rid of those changes. Meaning, we need to get rid of the files that were not added to staging area.

Unstaged content removal

How to get rid of changes made? In this case we need to restore back to a state where it should be exactly after committed. Meaning, we need to get rid of line number 5 which was added after committing.

go back to staging area of this branch

This line 5 will now be deleted with the command

git checkout -- initial-commit.txt

What is the meaning of this command?

Well, we know checkout is used to switch branches. When we switch to a branch, it means we get all the files in STAGING AREA OF THAT BRANCH into picture.

So when we do git checkout, it means we are showing the staging area of a particular branch.

So the above command git checkout -- initial-commit.txt means that, show me the staging area of this branch only (--) for the file initial-commit.txt. That means what ever file content of initial-commit.txt is in staging, that will be the initial-commit.txt.

In the above command -- is optional. So we can write

git checkout initial-commit.txt

Let's say we need to revert multiple files, then we can say

git checkout .

From git 2.23+ version

git restore .
deleting unstaged changes

What if I need to delete a newly created file which is not yet staged (not yet added to staging )?

Let's say I just created a file and not yet run add command. I need to delete that now.

Need to delete the unstaged (unadded) file

First list the files that need to be deleted. Then you can delete them

git clean -dn
Lists the files that need to be delted but won't delete

The above command just lists the files that could be deleted which are untracked files.

To delete them use the command below.

git clean -df
remove untracked files

Undo or Restore Staged Changes

We saw how to undo unstaged changes (where if the content is modified but not added to staged area even once then we can simply checkout all the files in staging area to bring it to focus so that all the changes made will be lost).

But let's say I make some changes to a file and add it to staging area. Now, in this case how to revert back. We can not do like we did above because, above, we just brought staging area files into focus where staging area was clean. But in this case, the staging area got polluted as we added a file into staging area.

If we think logically the solution would be

  • First unpollute or restore staging area back to normal. How? The lastest commit is what the expected staging area to look like correct? So we can make the latest commit as our staging area. This is possible by command

git reset filename // filename if not given, it will reset the entire staging area for all files
  • Now latest commit is what we have in staging area. Meaning, the file changes are unstaged at this point. Now we can remove unstaged file as learned in our previous content above

git checkout . // note that -A doesn't work here. . works

1st way / old way (less than git version 2.23) of removing staged files.

git reset filename  OR git reset
git checkout .

2nd way (git 2.23+)

git restore --staged filename 
git checkout .

Deleting commits

git reset helps to not only reset the staging area (makes staging area equal to latest commit) but also helps in deleting the commits by resetting the head from latest commit to old commit.

We have different types of deleting the commits.

  • soft reset

  • normal reset

  • hard reset

Soft reset

Undoing the commmit. It's like not doing commit at all. The general process we follow is

Creating a file + Adding files to staging area + Committing the files in staging area

But in soft reset we just get rid of last part and it's undoing the last part. The files get back to staging area and the commit gets deleted

Committing a file -> File changes comes to staging area -> File changes now in working directory

// To delete one commit

git reset --soft HEAD~1

// To delete two commits

git reset --soft HEAD~2

This can also be used to change or modify the commit message.

Normal reset

Undoing a commit + removing the changes from staging area. It looks like this

Committing a file -> File changes comes to staging area -> File changes now in working directory

// To delete one commit. The file changes becomes as it was working area 
(not staging area)

 git reset HEAD~1 

Hard Reset

Undoing a commit + removing the changes from staging area + working directory file changes. Basically, we remove everything until last commit. So the working directory becomes equal to previous commit. It's like we never wrote any code after previous commit.

Committing a file -> File changes comes to staging area -> File changes now in working directory

// To delete the latest commit and make the working area like previous commit

git reset --hard HEAD~1

Delete Branches

To delete a branch that is already merged into another branch

git branch -d branch-name

// to delete multiple which are already merged

git branch -d branch1, branch2, branch3

To delete a branch even though that is not merged (kind of force)

git branch -D branch-name

// to delete multiple branches

git branch -D branch1, branch2, branch3

Git Stash

Let's say I did a commit. Now, if I check the status, everything will be clean in the working directory. Now I will introduce some code, but suddenly I want to save these local changes and see how the work space was before when I didn't yet start the code after the commit.

One of the options to make the working directory equal to previous commit is by using the command git checkout . With this command I would get back my clean working directory like how it was after the commit and before starting to code locally but the biggest problem is I would lose the local changes which was not added to the staging yet.

Git stash solves this problem. It makes our working directory clean and resets back the working directory to how it was after the commit and before starting the work locally + it saves the local changes made in stashed area.

Let's do these steps

  • Now the working directory is clean and nothing is changed to add to staging area

  • Do some changes to file.txt

file.txt
Added this feature!
  • Now I want to see the workspace how it was after the previous commit, but at the same time I don't want to lose this above change to file.txt. So I can stash here

git stash
  • Now the file.txt has no content and the working directory is clean (nothing to be staged)

  • Let's say I want to bring back this stash to working directory

git stash apply
  • Let's add another feature to the file.txt

file.txt
Added this feature! // this was from stash 1

Added second feature! // this needs to be stashed now
  • Let's stash this also

git stash
  • Now we have two stash (1 with first line in file.txt and the lastest stash with both the lines in file.txt)

  • Let's see all the stash

git stash list

// The output will look like this
stash@{0}: WIP on master: 213977a merging detached-head-to-master-branch
stash@{1}: WIP on master: 213977a merging detached-head-to-master-branch
stash@{2}: WIP on master: 213977a merging detached-head-to-master-branch
stash@{3}: WIP on master: 213977a merging detached-head-to-master-branch
  • Note that you can't apply a stash when you are already in a stash. You either commit the current stash and then apply another stash OR you stash again and then apply the required stash.

  • Now let's apply stash 3. Note that stash 0 is always the latest stash

git stash apply 3
  • How to apply the latest stash and at the same time delete that stash

git stash pop <stash-number>
  • How to delete a single stash

git stash drop <stash-number>
  • How to delete all the stash

git stash clear
  • How to add meaningful message to stash

// instead of git stash, use the below command

git stash push -m "<your message here>"

Merging Branches

Two types of merges

  • Fast-forward merge

  • Non-fast-forward recursive merge

Fast forward merge

The feature branch is branched out from master. We then don't add commits to master until we merge back the feature.

Fast forward merge
Fast forward merge

Fast forward merge practical example

  • Created two commits m1 and m2 in master branch

master branch
  • Create a new branch called feature and switch to feature branch

feature branch created from master
  • Now let's switch back to master and merge feature into master

merged feature into master
Merged back feature to master

The above stuff is explained in a digramatic way

Fast foward merge

Squash merge

Now you see, when we merge feature branch back to master, we get all the commits of feature branch (f1 and f2 in this case) displayed in master. We might not want to know all the incremental changes happend in feature and we need all the commits of feature branch to be combined into one single commit in master when merged.

git merge --squash feature

This squash flag gets the latest commit of feature branch and put it to staging area of master. This let's you commit with your own message in master branch.

squash combines f2 into staging area of master

f1+f2 will be present in f2 commit. Merging with squash will add f2 to staging area of master. We can then commit that as we need into master as a normal commit. This keeps the master branch clean.

commit staging area to master
squash merge
squash merge commit into master branch

Non-fast-forward merge (Recursive merge)

This is generally used when, after creating a branch from master we would have additional commits in both master and the new branch.

recursive merge (non ff merge)

An additional commit is created in master after the merge of feature branch into master branch.

The latest merge commit holds all other commits below, and if you reset the head 1 time, then all the below commits will be gone.

//merge to master. This creates an extra commit
git merge --no-ff feature 
no-ff merge
no-ff creates an extra commit
hard reset of 1 commit undos the feature branch merge by removing all commits related to feature branch
non-ff merge diagram
combined commit non-ff

Git Rebase

We have seen two types of merge

  • Fast forward merge - If we create a branch from master -> add commits to feature --> Merge back to master (No changes was done on master until merged back.

  • Non Fast forward merge - If we create a branch from master -> add commits to master -> add commits to feature -> Merge back to master (There was a change in master after feature was created)

Now what is rebase?

Let's consider Non-ff case where we have created a feature branch, then we create a new commit in master as well.

We notice that we need this change done in master in our feature as well before we merge back feature into master. Then we can think of this rebase. In rebase we re-base (change the base of commit of master to one commit ahead) and then merge the copy of commits (not the original commits - so you should be careful before using it) from feature. This would be a fast-forward merge.

rebase

What actually happend here?

rebase - behind the scenes
Rebase
Rebase behind the scenes

Git will say "I want to base latest commits of feature branch on the latest commit of master branch."

Before Rebase, let's see the git log for both the branches

Master branch

master branch

Feature Branch

feature branch. Note we don't have m3 yet.

Note that, in feature branch, it branched out based on m2. Once we rebase, the feature branch would have rebased (will have m3 - latest commit of master) in feature branch. In the master, f1 will be the copy (hash will be different) of f1 feature.

Now, let's reabse

// I'm currently in feature branch

git rebase master

// this would create base as m3 in feature branch and f1 
// commit ID changes but content remains same
rebased from feature

Now let's merge feature into master. Since the latest commit in master (m3) is now the base commit in feature, the merge type would be Fast-Foward merge.

git merge (ff) after rebase
When to rebase
Caution with rebase

Git Cherry Pick

differences

Let's say I am working on master and make a typo in a commit. My colleague will create a branch out of master and start working, but then notices the typo caused. He will first correct the typo in the feature branch and commit it (single commit used to fix the typo). Then he does additional commits on his feature.

Later from master, I can cherry pick that particular commit that fixes the typo. Meaning, I can merge a single commit into my master which has the typo fixed. Note that, in cherry pick, the commit Id in master changes (it's a copy of the commit and not the same commit).

typo in master
ready for cherry pick

Now switch to master and type the command

git cherry-pick commit-id
cherry pick

GITHUB

Github content

Connecting Git and Github

git and github connection

The below command

git remote add origin

is used to connect git and github (let git and github know about each other). But to bring the content of git to github, we use git push and for getting github content to local we use git pull

push and pull

Origin and branch

Origin - Remote repo URL we would like to access

master / main - Remote branch we would like to push to

origin and remote branch

How git and github gets connected - Under the hood

With git remote add origin command, we establish a connection between local and remote repository. This connection can't be established until we push something to remote repo. The process involves some more steps.

role of remote-tracking-branch
git pull - git fetch and merge -> fetch updates the remote-tracking-branch with origin updates

Command git branch gives local branches. But git branch -a gives an extra entry of remote-tracking-branch. Remote-tracking-branch as said, gets created when we first type git push origin master command.

local and remote-tracking branch

Create branch locally and push to origin (remote)

Now that we know what is origin (remote), remote-tracking branch and so on, let's see

  • How to create a branch locally and push it to origin

  • How to create a branch in origin(remotely) and then pull it to local

How to create a branch locally and push it to origin?

Let's create a feature branch locally and then push it to remote.

git push origin feature in local creates remote branch
feature branch created in origin (remote)

How to create a branch in origin(remotely) and then pull it to local ?

branch created in origin

To see remote branch

git ls-remote

// a network call will be made to display the remote branch info
to see remote branch

To have the remote changes updated locally (in this case we need to get feature-remote-branch to local

git fetch origin
updated locally

Now with git fetch, the Remote tracking branch (Read only remote branch in local) gets updated but let' s say you add another commit remotely and then fetch again with git fetch, the local branch (which is local version of same branch) would not get updated, but the git pull updates local branch as well. Let's understand how.

local tracking branch

Local tracking branch and Remote tracking branch

local and remote tracking branch

To just use git push and git pull, without origin and branch name, we need to be on local tracking branch. To create local tracking branch below is the command. We don't need to worry about local and remote tracking branches but it's good to know how git and github gets connected using local and remote tracking branches. The name of local tracking branch and remote tracking branch must be same.

git branch commands
git branch commands

When we do a git clone, the local-tracking-branch is also setup by default so that we can use git pull and git push.

Upstream

Until now we manually created the local-tracking-branch and remote-tracking-branch which are what connecting the git and git-hub.

By using -u (upstream) while pushing, the local-tracking-branch and remote-tracking-branch gets created automatically.

git push -u origin master

Github Summary

github summary

Last updated

Was this helpful?