Step 1 − You modify a file from the working directory.
Step 2 − You add these files to the staging area.
Step 3 − You perform commit operation that moves the files from the staging area. After push operation, it stores the changes permanently to the Git repository.
Blobs: Binary Large Object
Trees
Commits
Branches
Tags
Clone
Pull
Push
HEAD
Revision
URL: URL represents the location of the Git repository. Git URL is stored in config file.
Git - Environment Setup
Installation of Git Client
1 2 3 4 5 6 7 8
# For Debian/ Ubuntu sudo apt install git-core git --version
# For RPM based distros [CentOS ~]$ su - [CentOS ~]# yum -y install git-core [CentOS ~]# git --version
Customize Git Environment
Git provides the git config tool, which allows you to set configuration variables. Git stores all global configurations in .gitconfig file, which is located in your home directory.
Setting User Infomation
These information are used by Git for each commit.
You pull the latest changes from a remote repository, and if these changes are divergent, then by default Git creates merge commits. We can avoid this via following settings.
[jerry@CentOS project]$ git config --globalcolor.ui true [jerry@CentOS project]$ git config --globalcolor.status auto [jerry@CentOS project]$ git config --globalcolor.branch auto
Setting default editor
By default, Git uses the system default editor. We can configure a different one by using git config.
1
git config--global core.editor vim
Setting default merge tool
Git does not provide a default merge tool for integrating conflicting changes into your working tree. We can set default merge tool by enabling following settings.
[user] email = shawncheunmail@gmail.com name = shawncheun [branch] autosetuprebase = always [color] ui = true status = auto brach = auto [core] editor = nano [merge] tool = vimdiff
Git - Life Cycle
General workflow is as follows:
You clone the Git repository as a working copy.
You modify the working copy by adding/editing files.
If necessary, you also update the working copy by taking other developer’s changes.
You review the changes before commit.
You commit changes. If everything is fine, then you push the changes to the repository.
After committing, if you realize something is wrong, then you correct the last commit and push the changes to the repository.
Shown below is the pictorial representation of the work-flow.
Git - Create Operation
In this chapter, we will see how to create a remote Git repository; from now on, we will refer to it as Git Server. We need a Git server to allow team collaboration.
Create New User
1 2 3 4 5 6 7 8
# add new group [root@CentOS ~]# groupadd devs
# add new user [root@CentOS ~]# useradd -G devs -d /home/gituser -m -s /bin/bash gituser
# change password [root@CentOS ~]# passwd gituser
Create a Bare Repository
Let us initialize a new repository by using init command followed by –bare option. It initializes the repository without a working directory. By convention, the bare repository must be named as .git.
1 2 3 4 5 6 7 8 9 10 11 12
[gituser@CentOS ~]$ pwd /home/gituser
[gituser@CentOS ~]$ mkdir project.git [gituser@CentOS ~]$ cd project.git/ [gituser@CentOS project.git]$ ls
[gituser@CentOS project.git]$ ls branches config description HEAD hooks info objects refs
Generate Public/Private RSA Key Pair Let us walk through the process of configuring a Git server, ssh-keygen utility generates public/private RSA key pair, that we will use for user authentication.
Note: Never share your PRIVATE KEY with others.
1 2 3 4
tom@CentOS ~]$ pwd /home/tom
[tom@CentOS ~]$ ssh-keygen // ~/.ssh was created.
Adding Keys to authorized_keys
Suppose there are two developers working on a project, namely Tom and Jerry. Both users have generated public keys. Let us see how to use these keys for authentication.
Tom added his public key to the server by using ssh-copy-id command as given below -
We have created a bare repository on the server and allowed access for two users. From now on, Tom and Jerry can push their changes to the repository by adding it as a remote.
Git init command creates .git directory to store metadata about the repository every time it reads the configuration from the .git/config file.
Tom creates a new directory, adds README file, and commits his change as initial commit. After commit, he verifies the commit message by running the git log command.
# Output the following result [master (root-commit) 19ae206] Initial commit 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644README
Tom checks the log message by executing the git log command.
1 2 3 4 5 6 7 8
[tom@CentOS tom_repo]$ git log
# Ouput the following result: commit19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <tom@tutorialspoint.com> Date: Wed Sep 1107:32:562013+0530
Initialcommit
Tom committed his changes to the local repository. Now, it’s time to push the changes to the remote repository. But before that, we have to add the repository as a remote, this is a one-time operation. After this, he can safely push the changes to the remote repository.
Note − By default, Git pushes only to matching branches: For every branch that exists on the local side, the remote side is updated if a branch with the same name already exists there. In our tutorials, every time we push changes to the origin master branch, use appropriate branch name according to your requirement.
# Ouput the following result: Counting objects: 3, done. Writing objects: 100% (3/3), 242 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To gituser@git.server.com:project.git * [new branch] master −> master
Now, the changes are successfully committed to the remote repository.
Git - Clone Operation
We have a bare repository on the Git server and Tom also pushed his first version. Now, Jerry can view his changes. The Clone operation creates an instance of the remote repository.
Jerry creates a new directory in his home directory and performs the clone operation.
# Produce the following result: remote: Counting objects:3, done. Receiving objects:100% (3/3), 241 bytes, done. remote: Total 3 (delta 0), reused 0 (delta 0)
Jerry changes the directory to new local repository and lists its directory contents.
1 2 3
[jerry@CentOS jerry_repo]$ cd project/ [jerry@CentOS jerry_repo]$ ls README
Git - Perform Changes
Jerry clones the repository and decides to implement basic string operations. So he creates string.c file. After adding the contents, string.c will look like as follows −
printf("string lenght of %s = %d\n", s[i], my_strlen(s[i]));
return0; }
He compiled and tested his code and everything is working fine. Now, he can safely add these changes to the repository.
Git add operation adds file to the staging area.
1 2 3 4 5
[jerry@CentOS project]$ git status -s ?? string ?? string.c
[jerry@CentOS project]$ git add string.c
Git is showing a question mark before file names. Obviously, these files are not a part of Git, and that is why Git does not know what to do with these files. That is why, Git is showing a question mark before file names.
Jerry has added the file to the stash area, git status command will show files present in the staging area.
1 2 3
[jerry@CentOS project]$ git status -s A string.c ?? string
To commit the changes, he used the git commit command followed by –m option. If we omit –m option. Git will open a text editor where we can write multiline commit message.
After commit to view log details, he runs the git log command. It will display the information of all the commits with their commit ID, commit author, commit date and SHA-1 hash of commit.
After viewing the commit details, Jerry realizes that the string length cannot be negative, that’s why he decides to change the return type of my_strlen function.
Jerry uses the git log command to view log details.
-int my_strlen(char *s) +size_t my_strlen(char *s) { char *p = s; @@ -18,7 +18,7 @@ int main(void) }; for (i = 0; i < 2; ++i) { - printf("string lenght of %s = %d\n", s[i], my_strlen(s[i])); + printf("string lenght of %s = %lu\n", s[i], my_strlen(s[i])); return 0; }
Git diff shows ‘+’ sign before lines, which are newly added and ‘−’ for deleted lines.
Git - Commit Changes
Jerry has already committed the changes and he wants to correct his last commit. In this case, git amend operation will help. The amend operation changes the last commit including your commit message; it creates a new commit ID.
Jerry modified his last commit by using the amend operation and he is ready to push the changes. The Push operation stores data permanently to the Git repository. After a successful push operation, other developers can see Jerry’s changes.
Before push operation, he executes the git log to view the commit, and git show command to review his changes.
Jerry is happy with his changes and he is ready to push his changes.
1 2 3 4 5 6 7 8 9 10 11
[jerry@CentOS project]$ git push origin master
# Output he above command will produce the following result:
Jerry’s changes have been successfully pushed to the repository; now other developers can view his changes by performing clone or update operation.
Git - Update Operation
Modify Existing Function
Tom performs the clone operation and finds a new file string.c. He wants to know who added this file to the repository and for what purpose, so, he executes the git log command.
The Clone operation will create a new directory inside the current working directory. He changes the directory to newly created directory and executes the git log command.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
[tom@CentOS~]$ cd project/ [tom@CentOS project]$ git log
commit19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <tom@tutorialspoint.com> Date: Wed Sep 1107:32:562013+0530
Initialcommit
After observing the log, he realizes that the file string.c was added by Jerry to implement basic string operations. He is curious about Jerry’s code. So he opens string.c in text editor and immediately finds a bug. In my_strlen function, Jerry is not using a constant pointer. So, he decides to modify Jerry’s code. After modification, the code looks as follows −
commit d1e19d316224cddc437e3ed34ec3c931ad803958 Author: Jerry Mouse <jerry@tutorialspoint.com> Date: Wed Sep 1108:05:262013+0530
Changed return type of my_strlen to size_t
commit19ae20683fc460db7d127cf201a1429523b0e319 Author: Tom Cat <tom@tutorialspoint.com> Date: Wed Sep 1107:32:562013+0530
Initialcommit
Jerry is happy with the changes and he wants to push his changes.
1 2 3 4 5 6 7 8 9 10
[jerry@CentOS project]$ git push origin master
# If Output, To gituser@git.server.com:project.git ! [rejected] master −> master (non-fast-forward) error: failed to push some refs to'gituser@git.server.com:project.git' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'Note about fast-forwards' section of'git push --help'for details.
But Git is not allowing Jerry to push his changes. Because Git identified that remote repository and Jerry’s local repository are not in sync. Because of this, he can lose the history of the project. To avoid this mess, Git failed this operation. Now, Jerry has to first update the local repository and only thereafter, he can push his own changes.
Fetch Latest Changes
Jerry executes the git pull command to synchronize his local repository with the remote one.
1 2 3 4 5 6 7 8 9 10 11
[jerry@CentOS project]$ git pull
# Output remote: Counting objects: 5, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From git.server.com:project d1e19d3..cea2c00 master −> origin/master First, rewinding head to replay your work on top of it... Applying: Added my_strcpy function
After pull operation, Jerry checks the log messages and finds the details of Tom’s commit with commit ID.
1 2 3
[jerry@CentOS project]$ git log
# Output
Now, Jerry’s local repository is fully synchronized with the remote repository. So he can safely push his changes.
1
[jerry@CentOS project]$ git push origin master
Git - Stash Operation
Suppose you are implementing a new feature for your product. Your code is in progress and suddenly a customer escalation comes. Because of this, you have to keep aside your new feature work for a few hours. You cannot commit your partial code and also cannot throw away your changes. So you need some temporary space, where you can store your partial changes and later on commit it.
In Git, the stash operation takes your modified tracked files, stages changes, and saves them on a stack of unfinished changes that you can reapply at any time.
1 2 3
[jerry@CentOS project]$ git status -s M string.c ?? string
Now, you want to switch branches for customer escalation, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run the git stash command.
1 2 3
[jerry@CentOS project]$ git stash Saved working directory and index state WIP on master: e86f062 Added my_strcpy function HEADis now at e86f062 Added my_strcpy function
Now, your working directory is clean and all the changes are saved on a stack. Let us verify it with the git status command.
1 2
[jerry@CentOS project]$ git status -s ?? string
Now you can safely switch the branch and work elsewhere. We can view a list of stashed changes by using the git stash list command.
1 2
[jerry@CentOS project]$ git stash list stash@{0}: WIP on master: e86f062 Added my_strcpy function
Suppose you have resolved the customer escalation and you are back on your new feature looking for your half-done code, just execute the git stash pop command, to remove the changes from the stack and place them in the current working directory.
1 2 3 4
[jerry@CentOS project]$ git status -s ?? string
[jerry@CentOS project]$ git stash pop
The above command will produce the following result:
1 2 3 4 5 6 7 8 9 10 11 12
# On branch master # Changed but not updated: # (use "git add ..."toupdate what will be committed) # (use "git checkout -- ..."to discard changes in working directory) modified: string.c
# Untracked files: # (use "git add ..."to include in what will be committed)
[jerry@CentOS project]$ git status -s M string.c ?? string
Git - Move Operation
As the name suggests, the move operation moves a directory or a file from one location to another. Tom decides to move the source code into src directory. The modified directory structure will appear as follows −
1 2 3 4 5 6 7 8 9 10 11 12 13
[tom@CentOS project]$ pwd /home/tom/project
[tom@CentOS project]$ ls README stringstring.c
[tom@CentOS project]$ mkdir src
[tom@CentOS project]$ git mv string.c src/
[tom@CentOS project]$ git status -s R string.c −> src/string.c ?? string
To make these changes permanent, we have to push the modified directory structure to the remote repository so that other developers can see this.
But after the pull operation, the directory structure will get updated. Now, Jerry can see the src directory and the file present inside that directory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
[jerry@CentOS project]$ git pull remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From git.server.com:project e86f062..7d9ea97 master−> origin/master First, rewinding head to replay your work on top of it... Fast-forwarded masterto7d9ea97683da90bcdb87c28ec9b4f64160673c8a.
[jerry@CentOS project]$ ls README src string
[jerry@CentOS project]$ ls src/ string.c
Git - Rename Operation
Till now, both Tom and Jerry were using manual commands to compile their project. Now, Jerry decides to create Makefile for their project and also give a proper name to the file “string.c”.
Now, other developers can view these modifications by updating their local repository.
Git - Delete Operation
Tom updates his local repository and finds the compiled binary in the src directory. After viewing the commit message, he realizes that the compiled binary was added by Jerry.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
[tom@CentOS src]$ pwd /home/tom/project/src
[tom@CentOS src]$ ls Makefile string_operations string_operations.c
[tom@CentOS src]$ file string_operations string_operations: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
VCS is used to store the source code only and not executable binaries. So, Tom decides to remove this file from the repository. For further operation, he uses the git rm command.
1 2 3 4 5 6 7 8 9 10 11
[tom@CentOS src]$ ls Makefile string_operations string_operations.c
To err is human. So every VCS provides a feature to fix mistakes until a certain point. Git provides a feature that we can use to undo the modifications that have been made to the local repository.
Suppose the user accidentally does some changes to his local repository and then wants to undo these changes. In such cases, the revert operation plays an important role.
Revert Uncommitted Changes
Let us suppose Jerry accidentally modifies a file from his local repository. But he wants to undo his modification. To handle this situation, we can use the git checkout command. We can use this command to revert the contents of a file.
[jerry@CentOS src]$ git status -s M string_operations.c
[jerry@CentOS src]$ git checkout string_operations.c Updated1 path from the index
[jerry@CentOS src]$ git status –s
Further, we can use the git checkout command to obtain a deleted file from the local repository. Let us suppose Tom deletes a file from the local repository and we want this file back. We can achieve this by using the same command.
[tom@CentOS src]$ ls -1 Makefile string_operations.c
[tom@CentOS src]$ git status -s
Note − We can perform all these operations before commit.
Remove Changes from Staging Area
We have seen that when we perform an add operation, the files move from the local repository to the stating area. If a user accidently modifies a file and adds it into the staging area, he can revert his changes, by using the git checkout command.
In Git, there is one HEAD pointer that always points to the latest commit. If you want to undo a change from the staged area, then you can use the git checkout command, but with the checkout command, you have to provide an additional parameter, i.e., the HEAD pointer. The additional commit pointer parameter instructs the git checkout command to reset the working tree and also to remove the staged changes.
Let us suppose Tom modifies a file from his local repository. If we view the status of this file, it will show that the file was modified but not added into the staging area.
# Modify file and view it’s status. [tom@CentOS src]$ git status -s M string_operations.c
[tom@CentOS src]$ git add string_operations.c
Git status shows that the file is present in the staging area, now revert it by using the git checkout command and view the status of the reverted file.
1 2 3
[tom@CentOS src]$ git checkout HEAD -- string_operations.c
[tom@CentOS src]$ git status -s
Move HEAD Pointer with Git Reset
After doing few changes, you may decide to remove these changes. The Git reset command is used to reset or revert changes. We can perform three different types of reset operations.
Below diagram shows the pictorial representation of Git reset command.
Soft
Each branch has a HEAD pointer, which points to the latest commit. If we use Git reset command with –soft option followed by commit ID, then it will reset the HEAD pointer only without destroying anything.
.git/refs/heads/master file stores the commit ID of the HEAD pointer. We can verify it by using the git log -1 command.
Commit ID from file is changed, now verify it by viewing commit messages.
1
jerry@CentOS project]$ git log-2
The above command will produce the following result.
1 2 3 4 5 6 7 8 9 10 11 12
commit29af9d45947dc044e33d69b9141d8d2dad37cc62 Author: Jerry Mouse <jerry@tutorialspoint.com> Date: Wed Sep 1110:16:252013 +0530
Added compiled binary
commit94f7b26005f856f1a1b733ad438e97a0cd509c1a Author: Jerry Mouse <jerry@tutorialspoint.com> Date: Wed Sep 1110:08:012013 +0530
Added Makefile and renamed strings.c to string_operations.c
mixed
Git reset with –mixed option reverts those changes from the staging area that have not been committed yet. It reverts the changes from the staging area only. The actual changes made to the working copy of the file are unaffected. The default Git reset is equivalent to the git reset – mixed.
hard
If you use –hard option with the Git reset command, it will clear the staging area; it will reset the HEAD pointer to the latest commit of the specific commit ID and delete the local file changes too.
Git reset command succeeded, which will revert the file from the staging area as well as remove any local changes made to the file.
1
[jerry@CentOS src]$ git status -s
Git status is showing that the file has been reverted from the staging area.
1 2
[jerry@CentOS src]$ head -2 string_operations.c #include<stdio.h>
The hard reset command also shows that the reset operation removed the local changes too.
Git - Tag Operation
Tag operation allows giving meaningful names to a specific version in the repository. Suppose Tom and Jerry decide to tag their project code so that they can later access it easily.
Create Tags
Let us tag the current HEAD by using the git tag command. Tom provides a tag name with -a option and provides a tag message with –m option.
Patch is a text file, whose contents are similar to Git diff, but along with code, it also has metadata about commits; e.g., commit ID, date, commit message, etc. We can create a patch from commits and other people can apply them to their repository.
Jerry implements the strcat function for his project. Jerry can create a path of his code and send it to Tom. Then, he can apply the received patch to his code.
Jerry uses the Git format-patch command to create a patch for the latest commit. If you want to create a patch for a specific commit, then use COMMIT_ID with the format-patch command.
[tom@CentOS src]$ git status -s M string_operations.c ??0001-Added-my_strcat-function.patch
The patch gets applied successfully, now we can view the modifications by using the git diff command. Show changes between commits, commit and working tree, etc
Branch operation allows creating another line of development. We can use this operation to fork off the development process into two different directions. For example, we released a product for 6.0 version and we might want to create a branch so that the development of 7.0 features can be kept separate from 6.0 bug fixes.
Create Branch
Tom creates a new branch using the git branch command. We can create a new branch from an existing one. We can use a specific commit or tag as the starting point. If any specific commit ID is not provided, then the branch will be created with HEAD as its starting point.
A new branch is created; Tom used the git branch command to list the available branches. Git shows an asterisk mark before currently checked out branch.
The pictorial representation of create branch operation is shown below −
Switch between Branches
Jerry uses the git checkout command to switch between branches.
In the above example, we have used two commands to create and switch branches, respectively. Git provides –b option with the checkout command; this operation creates a new branch and immediately switches to the new branch.
1 2 3 4 5 6 7
[jerry@CentOS src]$ git checkout -b test_branch Switched to a new branch 'test_branch'
Jerry decides to add support for wide characters in his string operations project. He has already created a new branch, but the branch name is not appropriate. So he changes the branch name by using –m option followed by the old branch name and the new branch name.
commit64192f91d7cc2bcdf3bf946dd33ece63b74184a3 Author: Jerry Mouse <jerry@tutorialspoint.com> Date: Wed Sep 1116:10:062013+0530
Added w_strlen functiontoreturn string lenght of wchar_t string
commit577647211ed44fe2ae479427a0668a4f12ed71a1 Author: Tom Cat <tom@tutorialspoint.com> Date: Wed Sep 1110:21:202013+0530
Removed executable binary
By viewing commit messages, Tom realizes that Jerry implemented the strlen function for wide character and he wants the same functionality in the master branch. Instead of re-implementing, he decides to take Jerry’s code by merging his branch with the master branch.
Now, the branch wchar_support has been merged with the master branch. We can verify it by viewing the commit message or by viewing the modifications done into the string_operation.c file.
The Git rebase command is a branch merge command, but the difference is that it modifies the order of commits.
The Git merge command tries to put the commits from other branches on top of the HEAD of the current local branch. For example, your local branch has commits A−>B−>C−>D and the merge branch has commits A−>B−>X−>Y, then git merge will convert the current local branch to something like A−>B−>C−>D−>X−>Y
The Git rebase command tries to find out the common ancestor between the current local branch and the merge branch. It then pushes the commits to the local branch by modifying the order of commits in the current local branch. For example, if your local branch has commits A−>B−>C−>D and the merge branch has commits A−>B−>X−>Y, then Git rebase will convert the current local branch to something like A−>B−>X−>Y−>C−>D.
When multiple developers work on a single remote repository, you cannot modify the order of the commits in the remote repository. In this situation, you can use rebase operation to put your local commits on top of the remote repository commits and you can push these changes.
Git - Handling Conflicts
Perform Changes in wchar_support Branch
Jerry is working on the wchar_support branch. He changes the name of the functions and after testing, he commits his changes.
[tom@CentOS src]$ git status -s M string_operations.c
[tom@CentOS src]$ git add string_operations.c
[tom@CentOS src]$ git commit -m 'Changed function name from w_strlen to my_wc_strlen' [master ad4b530] Changed function name from w_strlen to my_wc_strlen 1 files changed, 2insertions(+), 1deletions(-)
[tom@CentOS src]$ git push origin master
On the wchar_support branch, Jerry implements strchr function for wide character string. After testing, he commits and pushes his changes to the wchar_support branch.
{ while (*ws) { if (*ws == wc) return ws; ++ws; } returnNULL; }
After verifying, he commits his changes.
1 2 3 4 5 6 7 8 9 10
[jerry@CentOS src]$ git status -s M string_operations.c
[jerry@CentOS src]$ git add string_operations.c
[jerry@CentOS src]$ git commit -m 'Addded strchr function for wide character string' [wchar_support 9d201a9] Addded strchr function for wide character string 1 files changed, 10insertions(+), 0deletions(-)
[jerry@CentOS src]$ git push origin wchar_support
Tackle Conflicts
Tom wants to see what Jerry is doing on his private branch so, he tries to pull the latest changes from the wchar_support branch, but Git aborts the operation with the following error message.
As both Tom and Jerry changed the name of the same function, Git is in a state of confusion and it asks the user to resolve the conflict manually.
Tom decides to keep the function name suggested by Jerry, but he keeps the comment added by him, as it is. After removing the conflict markers, git diff will look like this.