Category Archives: Git

Everything you need to know to master git, Part 11, git rebase

Combining Git commits with squash

Imagine you have done lots of commits i.e. several commits for fixing a bug but you don’t need all of them and somehow you want to meld them and squash them into a single commit. You can use rebase. Git always squashes a newer commit into an older commit or “upward”. Let create some files:

Your tree would be something like this:

But now you would like to squash b-1, b2 and b-3 into one commit. So copy the hash ID of b-3 and execute rebase:

This will open your editor and the first few lines are:

Git will melt new commits into older one, and we want to squash b-3 and b-2 into b-1 so change it into:

Close all instance of your editor and save it. This will open another editor to write a new commit message.

Write your new commit message and save it. Your new tree should look like this:

Rebasing branches on to the base

When you create a feature branch and start working on that, your master might grow and also your branch. Let’s create a repository and add “a” and b:

Then lets create a feature branch for “c”

Now lets get back to master and “d” and “e”:

At a certain point you will decide to merge to the master branch your tree looks like this:

By doing this you will the following tree, which in a big project might become very complicated and difficult to track.

As you can see the branch feature-c is 2 commits behind of the master branch.  You can rebase your feature branch onto the master branch so you can make it like that your feature branch has just branched from your master.

By doing so, you finally end up with a “linear history”. You can also give tags to your commits so you make your tree more readable:

Rebase: rewriting Git history reword, delete, reorder  and Split

Refs 1,2, 3, 4

Everything you need to know to master git, Part 13, View git history (commit log) of specific lines of code in a file

git blame

The git blame command is used to know who/which commit is responsible for the latest changes made to a file. The author/commit of each line can also been seen. git blame does not show the per-line modifications history in the chronological sense. It only shows who was the last person to have changed a line in a document up to the last commit in HEAD.

Line between 2 and 4:

2 lines after 2:

Make the output shorter:

Only display email of the auther:

get the blame for a specifi commit:

When the commit ID is 00000000000 it means I have changed that line locally.

git blame does not show the per-line modifications history in the chronological sense. It only shows who was the last person to have changed a line in a document up to the last commit in HEAD.
In order to see the full history/log of a document line, you would need to run a git blame path/to/file for each commit in your git log. Since Git 1.8.4, you can use git log has -L to view the evolution of a range of lines.

Refs: 1, 2

Everything you need to know to master git, Part 12, Setting up your home Git server

In this tutorial I will show you how to set up your own Git server. Here I have used an Ubuntu 20.04 server on a virtual machine which is called homeserver and my clinent machine is called client. The convention for the bash scripts is user@host:~$

The Protocols

Git can use four distinct protocols to transfer data: Local, HTTP, SSH and Git

Local

The URLs will be in the following form:

or

HTTP Protocols

Git can communicate over HTTP using  Smart HTTP or Dumb HTTP.

Smart HTTP

Intelligently negotiate data transfer in a manner similar to how it does over SSH. Very similarly to the SSH or Git protocols but runs over standard HTTPS ports and can use various HTTP authentication mechanisms, meaning it’s often easier on the user than something like SSH, since you can use things like username/password authentication rather than having to set up SSH keys. It can be set up to both serve anonymously like the git:// protocol, and can also be pushed over with authentication and encryption like the SSH protocol.

Dumb HTTP

It is very simple and generally read-only. If a server does not respond with a Git HTTP smart service, the Git client will try to fall back to the simpler Dumb HTTP protocol. The Dumb protocol expects the bare Git repository to be served like normal files from the web server.To set up all you have to do is put a bare Git repository under your HTTP document root and set up a specific post-update hook.

The post-update hook that comes with Git by default runs the git update-server-info to make HTTP fetching and cloning work properly. This command is run when you push to this repository (over SSH for example). You can clone via:

SSH Protocol

To clone a Git repository over SSH, you can specify an ssh:// URL like this:

Or you can use the shorter scp-like syntax for the SSH protocol:

The main drawback is that you need SSH authentication even for read only repositories.

Git Protocol

It is a daemon (that comes packaged with Git) and  listens on a dedicated port (9418) that provides a service similar to the SSH protocol, but with absolutely no authentication. In order for a repository to be served over the Git protocol, you must create a git-daemon-export-ok file. Either the Git repository is available for everyone to clone, or it isn’t. You can enable push access but, given the lack of authentication, anyone on could push to your repository. Generally, you’ll pair it with SSH or HTTPS access for the few developers who have push (write) access and have everyone else use git:// for read-only access. This protocol requires xinetd or systemd configuration or the like and also requires firewall access to port 9418,

Now let’s set up our server:

1) First on the server side make sure that the essential packages are installed:

2) Create a group for Git users:

3) Create users and add them to the gituser group:

BTW If you want to:
List all groups:

List groups of a user:

List all users:

And if you want to complelty delete a user and remove the user home directory:

Hint: the user should do a login and logout to make the group assignment effective.

4) Create a git repository and tell it to share based on the group the user belongs to

BTW if you want to create a non-bare repository that you can push into that:

5) Give gituser group permission to access the repository directory

Hint:
All files in Linux belong to an owner and a group. chgrp command changes the group ownership of a file or directory. You can set the owner by using chown command, and the group by the chgrp command. -R means do it recursively. You can see the owner and permision by

This command will give the gituser group the permission of read and write.

chmod g+s sets the setgid mode bit on the current directory which will cause any new file or directory that placed in this directory inherit the group owner (gituser), but the current files and directories will not be effected. To aply changes on them we directly call the command on them by using find /home/git/repos1 -type d

6) Create and config the clients
Create two users on the client machine:

SSH key pairs can be used to authenticate a client to an SSH server. That’s why we used ssh-copy-id to add them into ~/.ssh/authorized_keys so we can log in without password later.

Now copy the public key to the server and append it to the authorized_keys file as above


Append the client’s public key for ceach client to the authorized keys on server

Now you should be able to clone the repository:

And you should be able to push:

and pull the changes with other user:

If you want to see the URL that your local Git repository was originally cloned from:

Refs: 1, 2, 3, 4, 5, 6

Everything you need to know to master git, Part 7, git branch

It quite often happens that a software product is on a git server and developers add new features to the product. To make the job easier, developers usually create branches beside the master branch (which is the default branch) and work on their branch and once the changes are confirmed and tested, they will be merged to the master branch. So let’s get started:

ok now let’s create two branches, one for developer1 and one for developer2,

let’s make some work on the first branches:

and some changes in the second branch:

now you should see this in git gui or if you type:

Now in the git gui, go to merge>local merge and choose b1, you will get the following error because of the merge conflict:

so call the following to solve the conflict:

choose C and save and then:

 

Working with branches in nutshell:

Creating a branch:

or short version:

Track a Remote Branch:

If you have already created a local branch and you want to track it with remote branch:

Push a branch and automatically set tracking.  you can use the -u flag (upstream) when you make your first push:

This only pushes your master branch to origin

pushes all your branches to origin

 

View branches

To view both remote-tracking branches and local branches

To see only remote branches:

or

or

To see the tracked branches:

Deleting both local and remote branch

local and remote branches actually have nothing to do with each other, even if you’ve established a tracking connection. They are completely separate objects in Git and deleting one wouldn’t delete the other If you want any branch item to be deleted, you need to delete it explicitly.

To delete a local branches:

-D, which is an alias for –delete –force, which deletes the branch “irrespective of its merged status

To delete a remote branch:

As of Git v1.7.0, you can delete a remote branch using:

summary:

In most cases the remote name is origin.

Pushing into the wrong branch

 

Ref [1], [2], [3]

Everything you need to know to master git, Part 6, git stash

Sometimes you have made some changes into your local copy and you don’t want to make a commit because your code doesn’t compile, but something came up and you should change your branch for instance and you will lose your local changes. In this situation, you can stash your changes and save your changes and change your branch for instance. The command saves your local modifications away and reverts the working directory to match the HEAD commit.

Let’s have a complete example:

now we make some change:

now let’s stash changes:

stash@{0} is the most recently created stash, stash@{1} is the one before it. WIP stands for work iprogress. We can list and drop changes by:

We can use gitk to see the changes and :

Now let’s create a branch and one more stash and apply the previous stash from the master branch there:


To see all stash in gitk

To checkout a specific file from stash:

To view the file in the stash:

stash@{0}^1 means the first parent of the given stash, which as stated in the explanation above is the commit at which changes were stashed away.

 

Everything you need to know to master git, Part 5, git cherry-pick

To illustrate what is cherry-pick let’s imagine  the following situation: you and another developer start working on project, you will both make some changes, but after a while the other developer find a bug in the code and you are interested in to only apply that hot-fix without taking other changes from another developer, in this situation you should do a cherry pick, so let’s do it:

and you continue with something like:

The other developer checks out the repository and will start working and make the second commit:

and in the third commit, he fixes the problem with A.txt so he would something like this:

And he may continue his work with another commit:

Now you are only interested in the third commit and you don’t want to add other files to your local copy so you have to cherry pick the third commit, so all you have to do is fetch all changes, find the corresponding SHA1 value for the third commit and apply the cherry pick.

And now the file A.txt in repose1 should be same as the file in repose2 while C.txt and D.txt are not added to repose1.

 

 

 

Everything you need to know to master git, Part 4, git merge, git diff with GUI tools

Before following this post, make sure you have already set up everything from my other post regarding the configuration of git environment.

Diff

Diff command will allow you to compare two different commits, let’s set up a repository:

Now in the second repository, we make some changes:

now let’s back to the first repository and see the changes:

now you should see all the difference in meld:

To find diff between current and previous commit:

As of Git 1.8.5, @ is an alias for HEAD, so you can use:

If you want to know the diff between head and any commit you can use:

You can add -t to select your difftool such kdiff3, meld, etc.
To see the diff with gitk, first go to Edit>Preference and set the external difftool.
Now click on your revision, then right-click on the revision that you want to compare to and click on Diff this-> selected. Now on the bottom window right click on any of changed file and select External diff

Merge and conflict

Now let’s merge all the changes. If you make some changes on different files you will do fast-forward. That means you only incorporate new changes and everything is ok afterward. But if you made some changes on the same file in same lines then you have a merge conflict and you should do a three-way merge.

To show you the point let’s make some changes in the first repository in the file A.txt.

now if you call git merge, you will get a merge conflict, to solve this call the following:

and you will get this in meld:

and this in kdiff:

+————————————–+
| LOCAL | BASE | REMOTE |
+————————————–+
|                MERGED               |
+————————————–+

LOCAL: Your side of the conflict – ie, your branch (HEAD) that will contain the results of the merge
REMOTE: The remote side of the conflict – the branch you are merging into HEAD.
BASE: The common ancestor of both local and remote. useful for feeding into a three-way merge tool
MERGED: The result of the automatic merge.

go through the conflict and choose the side (A, B or C) and see the merged file in the window below and save and close.

Use command line and interpret the output of git diff

diff uses Levenshtein distance and tries to determine the smallest set of deletions and insertions. The output is called a “diff”, or a patch, since the output can be applied with the Unix program patch

The POSIX standard specifies the behavior of the “diff” and “patch” utilities and their file formats. You can call the following to see the difference between and two files or directories:

For instance in our case:

-u  indicate unified format

Git also uses the unified format. If you call

You will get the following:

Let’s interpret the output:
1) The first line diff --git a/A.txt b/A.txt is a “git diff” header in the form diff –git a/file1 b/file2. The a/ and b/ filenames are the same unless rename/copy is involved. The –git is to mean that diff is in the “git” diff format.
2) In the last line in extended diff header is:

100644 is the mode of given file which means that it is ordinary file and not e.g. symlink, and that it doesn’t have executable permission bit, and about shortened hash of preimage (the version of file before given change) and post image (the version of file after change).
3) Next is two-line unified diff header

The original file is preceded by “” and the new file is preceded by “+++“.

4) Following this are one or more change hunks that contain the line differences in the file. Unified format hunks starts with line like:

5) Next comes the description of where files differ. The lines common to both files begin with a space character. The lines that actually differ between the two files have one of the following indicator characters in the left print column:

  • ‘+’ — A line was added here to the first file.
  • ‘-‘ — A line was removed here from the first file.

 

Refs 1, 2, 3

Everything you need to know to master git, Part 3 (git checkout and git clean)

Cleaning up git environment and rollbacking local changes

Once you started working on your local copy you might mess everything and need to clean up or roll back changes for some file, you can use git checkout and git clean

to rollback changes for a particular file:

to rollback changes for the entire local copy while keeping added files:

 

if you just want to remove the file and directories that have been added but not staged:

-d
Remove untracked directories in addition to untracked files.

–dry-run
Don’t actually remove anything, just show what would be done.

-i
Show what would be done and clean files interactively. See “Interactive mode” for details.

This will create a new branch and switch to that:


 

Everything you need to know to master git, Part 2, Undo changes, git revert, reset amend

Undo local changes

Git reset

How to undo a commit in Git when no one has pulled your changes? The answer is git reset
git reset will do this job for you, it has three options, hard and soft and mixed.

1) Mixed:  This is the default action. Resets the index1 but not the working tree2 (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated.

2) Soft: Does not touch the index file or the working tree at all but resets the head to <commit>. This leaves all your changed files “Changes to be committed”

3) Hard: This option will reset the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded.

1 The git “index” is where you place files you want to commit to the git repository. Some names you may have heard:

  • Index
  • Cache
  • Directory cache
  • Current directory cache
  • Staging area
  • Staged files

Files in the git index don’t get committed to the repository until you use the git commit command.

2 Working tree is what is actually in the files that you are currently working.

git ls-tree only works with git refs, e.g.

Commit could be in one of the following forms:
HEAD{2}
HEAD~2
commit-SHA

Examples:

You can undo your reset by

Since after your first reset your previous HEAD is at one commit before the current one.

git log will be exactly the same as before, but git relog will show you that you have reset the HEAD.

A Complete setup

some important note:

git add -A is equivalent to

Let’s continue our work:

 

The git reflog command records a chronological history of everything you have done in your local repository. Its full output might look like this:

C.txt and D.txt will remain (soft)

C.txt and D.txt will be deleted.

To see which files have been added and status:

Finally, lets’ push changes by:

Git revert

How to undo a push in Git when someone might have pulled your changes? The answer is Git revert. If you’ve already pushed your changes and someone else pulled in those changes, you should not use git reset to undo changes, use git revert instead.

Examples:

Let’s follow our work:

amend

This will fix your most recent commit message

Example


[1]

Everything you need to know to master git, Part 1, setting up git environment, GUI, mergetool, difftool

Installing prerequisite

Configuring Git Environment

First config your environment and set a proper merge/ diff tool.  The settings in Linux are under/home/<username>/.gitconfig.    If you don’t know where this file is located in your OS You can easily edit this file by:

All Git setting can be set or viewed via terminal, i.e:

To view all settings

To view current settings of mergetool:

To set them

All available options for setting a merge tools can be listed by:

here we config it for meld, kdiff3 and vim.

Kdiff3

Add the following to your .gitconfig file

For Windows users:

 

Meld

For Windows users:

 

Vim

Username, Email and editor

 

Adding Alias

[alias]

SSH key

and upload your public key
/home/<username>/.ssh/id_rsa.pub
into your GitHub account

Push into GitHub without password using ssh-key

Do the following:

For Example if your repository is:

Run the following:

How to show current branch in Bash prompt

Edit ~/.bashrc and find the following lines and uncomment them

and replace them with

1, 2