Git
There are three parts which are present in our project directory -
- Working tree - This is bascially all the files and folder which are there in our project folder.
- Staging area / Index - This is a place where the files which have been changed by us are present. We need to add those files in the staging area so that it can be further commited to the local repository.
- Local repository - This is the place which contains the commit. It is bascally a snapshot of our project.
NOTE : Local repository and staging area/index are stored in .git folder which is present in our project directory.
-
To link a local repository with a remote repository, we need to add the remote.
git remote add origin <url>
-
To push our commits from local repo to a remote repo
git push -u origin <branch name>
The -u flag is used to set origin as the upstream remote in your git config. As you push a branch successfully or update it, it adds upstream reference.
As you push local branch with git push -u option, that local branch is linked with the remote branch automatically. The advantage is, you may use git pull without any arguments.
- Git uses Directed Acyclic Graph(DAG) to represent commit history. In these graphs a commit always points towards its parent.
- Git IDs are the name of git objects (like commit objects which are used to store information about commit)
It is of 40 character generated by SHA-1 (Secure Hash Algorithm). It can be genereted by user too.
git hash-object <filename>
-
References are used to refer to commits or other things like branches. Eg. HEAD -> master (Here master is a BRANCH LABEL and it always points to the latest commit in that branch and HEAD is a symbolic reference which points to the current commit in a repository). So branch label always refer to the latest commit in a branch and HEAD refers to the current commit in a repo.
git show HEAD
git show master
These will return SHA-1 value of commits -
Use of '~' and '^' ---> ~ is used to refer to the parent of a commit. eg. HEAD~ or HEAD~1 will refer to the commit done before the current commit. ^ is used to refer to the parent of merged commit (merged commits have two parents). eg. HEAD^2 or HEAD^^ will return the second parent of a merge commit. Similarly HEAD^1 will return first parent.
git show HEAD~
git show HEAD^
These will return same values. -
Git tags : Two types -
-
Simple tags-
git tag <tagname> [<commit>]
by default, if commit is not mentioned then tag is applied to the recent commit. -
Annotated tags-
git tag -a -m <message> <tagname> [<commit>]
This will create a git annotated tag object with more information about a commit.
-
Git Branches :
-
git branch <branchname>
: This creates a branchgit checkout <branchname>
: This checks out a branch which means the HEAD reference now shifts to the mentioned branch. -
Above two statements can be combined to perform creating and shifting to a branch in single statement.
git checkout -b <branchname>
(This won't work if a branch is already created) -
Detached HEAD state : When we wish to move to an older commit, we simply check it out. The HEAD reference then points to that commit's SHA-1 value instead of pointing to a branch. This state is called detached HEAD state. If you wish to make any changes from here then you will have to create a new branch. eg.
git checkout HEAD~
NOTE : Working tree will be updated according to the branch we're currently on. If master doesn't contain new changes then all the changes from new branch will disappear if we switch to master.
-
Delete branch : Deletes a branch.
git branch -d <branchname>
-
Dangling commits : Let's suppose we create a branch and commit some changes in it but we haven't merged it into master yet. Now if we delete that branch then git will return an error saying this branch contains some commits which are not merged yet. This means once we delete the branch then all the commits in it will be lost which will eventually be garbage collected by git. If we still wish to delete that branch then -
git branch -D <branchname>
Git Merge :
-
Fast Forward merge - In the merge, the master branch label directly moves to the latest commit of the branch we want to merge. This makes the whole branch as master branch. For this to happen, there shouldn't be any commit after the point from where we started another branch because if there were some commits then master branch would jump from latest commit to another branch's latest commit and commits between the starting point of another branch and the lastest commit on the master branch will get lost.
git merge <branchname>
-
Merge commit - In this merge we don't convert another branch into the master branch directly but we simply join the two branches at some other point. In this way we get a triangular DAG. This is useful when there are more commits after the starting point of another branch. But we can also use this method even if there are no commits after the initial point.
git merge --no-ff <branchname>
Here --no-ff means no fast forward merge.
NOTE - Fast forward merge is the default merge. Git only switches to merge commit if there are more commits after another branch's initial point. Moreover, we can do a merge commit with --no-ff
even if fast forward merge is possible.
Git merge conflicts :
Sometimes when we work on different branches, we modfiy the same file or a hunk in a file in different ways. This makes it impossible for git to know which change to keep. In this type of situaton we need to solve these conflicts manually. If we run git status
after git returns a merge conflict error, it shows us the file which needs to be fixed under 'both modified'. We can fix these conflicts in our editor. Git marks the area which needs fixing by '>>>>>>> and <<<<<<<<'.
Tracking branch :
-
Tracking branch is basically a local branch which keeps a reference to the remote repository. Every remote repository with one or more commits has a default branch (eg. master). When we clone a repository then git automatically creates a tracking branch which points to the latest commit in the remote repo. Syntax -
<remote>/<branchname>
This branch gets updated only when we use a network command like fetch, pull or push (not before that). -
If we view all the branches by
git branch --all
, we should see a local branch, a tracking branch and a symbolic reference to the default tracking branchremotes/origin/HEAD -> origin/main
. This symbolic reference is just to define the default tracking branch. We can change this reference bygit remote set-head <remote> <branch>
. This reference makes our life easier because if we were to use 'origin/master' somewhere then we could simply write 'origin' in place of that. -
Now if we use
git status
after commiting in local repository, git returns 'Your branch is ahead of 'origin/master' by 1 commit '
Network Commands : As we talked about tracking branches, we should know that these branches won't update on their own. They need some commands to fetch new commits from remote repo. This is where network commands come into the use. These are of 4 types -
-
Clone - Clones a remote repository.
-
Fetch - This command is used to fetch new commits from remote repository. It bascially fetches all the new commits by other users.
git fetch
This updates the tracking branch and it points to the new commit while our master branch still remains at the same position. -
Pull - This command adds the functionality of merging new commits from remote repo. It bascially fetches and merges new commits from remote repository.
git pull
By default, fast forward merge. We could use --no-ff for merge commits. -
Push - Pushes local commits into remote repository.
Rebase: This technique is used to place a branch from its original parent to the tip(base) of a branch. Because parent of commits is changing, so does their own SHA-1 values. Once we rebase a branch, all the commits of that branch get new SHA-1 values.
This can involve merge conflicts which we have to take care of.
If we need to rebase a branch, then first checkout that branch and use git rebase <base branchname>
If we run into merge conflicts, then after fixing and adding the changes to stage area, we need to write git rebase --continue
Amend a commit:
This command is used to amend a commit. Modify the file accordingly, add changes to staging area and use :
git commit --amend -m "<message>"
Interactive Rebase:
It is used to edit commits. This will change the commit history, so use it carefully and maybe avoid it if working shared branch environment.
git rebase -i <after-this-commit>
It has several options to edit a commit like sqaush, edit, reword etc.
Once you execute this command, git will open a text editor. You can write any option from the list before a commit.
Squash Merge:
Merges the tip of a branch into the another branch's tip. This will help in getting a linear commit history which means the previos commit history will be lost.
git merge --squash <branch-to-be-merged>