ThinkingCog

Articles written by Parakh Singhal

A short note on Git Cherry-Pick

Key Takeaway

In the real world, situations arise where some urgent work is done on a codebase that is required to be released to mainstream users. This work then needs to be bought down into the feature branches to maintain parity with the master branch.

In Git, we have the command of Rebase to handle such a situation, but in some cases, a more surgical approach is required, whereby, only the commit having the changes is required to be applied to the feature branch and exclude anything else which might otherwise pollute the feature branch. This is what Git’s cherry-pick command achieves.

Read On

One of the founding premises of Git is to commit often and commit early. This aspect of the foundation enables you, the user, to see the evolution of your work over time and enables Git to take the responsibility to manage the evolution in a discreet order via commits. By having small commits, you enable Git to recover your work in a granular manner, should the situation come to be, as desired. And dovetailing this practice of small and frequent discreet commits, revolves the power of the command of cherry-pick.

Git’s cherry-pick command helps you apply a commit in a surgical manner such that it only brings in the changes corresponding to a commit to the branch under concern. This command can be thought to be useful in scenarios whereby you have to bring in a limited number of changes that are pertinent to your work. Examples:
1. There’s a hotfix branch created from the master branch and you need to include the hotfix in your feature branch.
2. There are a couple of commits that you need to take from some other feature branch and incorporate in your branch

Let’s take a look at an example to understand the cherry-pick command.

$ mkdir cherrypick
$ cd cherrypick
$ git init
$ touch file1.txt
$ printf "first line" > file1.txt
$ git add .
$ git commit -m "First commit in master branch"
$ printf "\nsecond line" >> file1.txt
$ git commit -am "Second commit in master branch"
$ printf "\nthird line" >> file1.txt
$ git commit -am "Third commit in master branch"
$ git branch develop
$ git checkout develop
$ touch file2.txt
$ printf “first line” >> file2.txt
$ git add .
$ git commit -m “First commit in develop branch”
$ printf “\nsecond line” >> file2.txt
$ git commit -am “Second commit in develop branch”
$ printf “\nthird line” >> file2.txt
$ git commit -am “Third commit in develop branch”
$ git checkout master
$ git log --oneline develop
$ git cherry-pick SHA1 hash of the second commit from the develop branch

 

Resolve any conflicts that arise from the cherry pick here and then run a git log

$ git log --oneline master

 

In this example, we created a directory aptly named cherrypick and then inserted some text entries into a text file named file1.txt. We then created a branch called develop from the master branch. In the develop branch we created some entries in a text file named file2.txt and then moved back to the master branch. Now listed the log entries from the develop feature branch and noted down the SHA1 hash of the commit corresponding to which we want to pick the changes from the develop branch. In this case, we picked the second commit from the develop branch.

01 Creation of repoFigure 1 Creating the repository and some initial commits

02 In Develop BranchFigure 2 Creation of a branch and some initial commits

03 In Develop BranchFigure 3 Going back to the branch where a cherry-picked commit needs to be merged

Finally, we used Git’s cherry-pick command in the master branch in conjunction with the SHA1 hash noted before. This will bring in the changes from the develop branch into the master branch that were made corresponding to the commit hash. In doing so, you may be required to resolve any conflict that may arise.

04 In Master BranchFigure 4 Process of cherry picking may throw up some conflicts which would require intervention

05 In Master BranchFigure 5 Once a commit is cherry picked, its content will then become available in the target branch

This way we can literally cherry-pick the changes corresponding to specific commit(s) in a branch and bring them to the branch of our choice.

Hope this was helpful.

Git Rebase vs. Merge

Introduction

Every version control system offers a core set of functionalities, of which, the ability to create branches and then merge changes into branches are offered by both central and distributed version control systems. The way different systems behave is different and hence, often, while the result will be the merging of changes, the way it accomplished and the resulting history created are different.

Consider the following scenario:
You have created a feature branch from a long-running branch. Someone in the team commits in changes into the parent long-running branch and you have to bring in the changes into the feature child branch that you are using for active development. There are two ways to do that in Git:
1.    Merge command
2.    Rebase command

Both the commands achieve the same outcome of integrating the changes from parent long-running branch into the feature child branch. Where things differ, is the resulting commit history that gets created due to the usage of the commands.

Merge command

In the aforementioned scenario, if you use the merge command, the resulting commit history will bear a merge commit. Note that the merge command does not alter the history of your feature branch in any way. On the contrary, the merge commit provides a context for the changes bought from the parent branch.
Run the following commands in bash shell to simulate a merge in git to bring in changes from a parent branch to a child branch:

$ mkdir Merge
$ cd Merge
$ git init
$ touch file.txt
$ printf “First instalment of work done in master branch” > file.txt
$ git add .
$ git commit “First commit in master branch” 
$ git branch dev
$ git checkout dev
$ printf “\n\nWork done in dev branch” >> file.txt
$ git commit -am “First commit in dev branch”
$ git status
$ git checkout master
$ printf “\n\n\n\nSecond instalment of work done in master branch” >> file.txt
$ git commit -am “Second commit in master branch”
$ git checkout dev
$ git merge master
Resolve conflicts, if any. Perform a git commit which is going to be a merge commit.
$ git log –oneline –graph

The result will be something like shown in the image below:

 

Log history after a merge. Notice the merge commit made in the end

Figure 1 Log history after a merge. Notice the merge commit made in the end

Rebase command

Rebase command, as the name suggests re-creates the base for the child branch while bringing in the changes from the parent branch. This results in a cleaner, unidirectional history, but the context under which the changes were bought in, gets lost. It appears that the child feature branch always worked with the changes bought from the parent long-running since the beginning of its creation.

Run the following commands in bash shell to simulate a rebase in git to bring in changes from a parent branch to a child branch:

$ mkdir Rebase
$ cd Rebase
$ git init
$ touch file.txt
$ printf “Work done in master branch” > file.txt
$ git add .
$ git commit -m “First commit in master branch”
$ git branch dev
$ git checkout dev
$ printf “\n\nWork done in dev branch” >> file.txt
$ git commit -am “First commit in dev branch”
$ git checkout master
$ printf “\n\n\n\nSecond instalment of work done in master branch” >> file.txt
$ git commit -am “Second commit in master branch”
$ git checkout dev
$ git rebase master
Resolve conflicts, if any. Perform a git add . followed by git rebase –continue
$ git log –oneline –graph

 

The result will be something like shown in the image below:

Log history after a rebase. Notice the second commit from master inserted as a base commit for dev branch

Figure 2 Log history after a rebase. Notice the second commit from master inserted as a base commit for dev branch

Conclusion

The net outcome of both the rebase and merge command as seen above is the same i.e. integration of changes from one to another branch, here parent to child branch.
 
Now, naturally, the question arises as to when to use what.
 
Merge is a non-destructive operation that preserves the chronological order of commits verbatim. The merge command creates a merge commit which brings in a convergence point into the commit history, thereby, bringing in the context under which the integration of changes occurred. This is essential when you are working on a public project and want every developer to have a shared context.
 
Rebasing a feature branch with changes bought from the long-running parent branch creates a clean linear history in the feature branch. But this eliminates the context under which the activity of rebasing was done. It is preferred when you are working as part of a small team and, it is relatively easy to collaborate and communicate with all the developers about the changes done to the feature branch.

References

1.    https://git-scm.com/book/en/v2
2.    https://www.atlassian.com/git/tutorials/merging-vs-rebasing
3.    https://medium.com/datadriveninvestor/git-rebase-vs-merge-cc5199edd77c

How to Operate Multiple GitHub Accounts from a Single Computer

Developers love to use a single computer for all their needs, whether they are related to office work or work on their personal projects. Since using version control is a cardinal requirement in any software project, personal or professional, it becomes imperative that a requirement arises, whereby, a developer is required to operate multiple GitHub accounts from a single computer.

Consider a scenario that you have two GitHub accounts – one sponsored by your employer and the other one personal and you want to use a single computer to operate both of them. This is possible by virtue of SSH keys and setting remote repositories under the correct SSH key. The following is the broad outline of the article:

1. Setting up SSH keys

2. Setting up a configuration file to use easily co-ordinate among multiple accounts

3. Setting remotes correctly

1. Setting up SSH Keys

SSH keys generated using RSA algorithm, generates a pair of keys – public and private. Per the norm, the private key remains secure with you and never travel over the wire, while public key is distributed for verification. In this case the public key is stored in your GitHub account.

On Window 10 open Git Bash, navigate to C:\Users\YourMSID\.ssh and key in the following command:

$ ssh-keygen -t rsa –b 4096 -C "your personal email address"

 

This will prompt you to create a new file. Provide a suitable name to the file so you are able to differentiate between various files and hence various keys. Also make sure to enter a suitable passphrase. Providing passphrase further encrypts the private SSH key using a symmetric encryption algorithm, and will render the theft of the private key useless.

Now generate a key-pair for the official account in a manner similar to described above:

$ ssh-keygen -t rsa –b 4096 -C "your official email address"

 

The next step after the generation of the key-pair will be to copy over the public SSH keys into respective GitHub accounts. I am assuming a generic name generally given to key-pair given to personal accounts. Print on the screen and copy the relevant section:

$ cat id_rsa_personalaccount.pub

 

Make sure that you copy the key that starts with “ssh-rsa” and ends with gibberish. Do not copy the email part.

Now navigate to your personal GitHub account and open account settings. Open the “SSH and GPG Keys” section. Click on “New SSH Key”, and copy over the key. Make sure to give a suitable title to remember the computer that the key is present on. You may generate a key on some other computer tied to your personal account. A suitable title will help you connect the key and the originating computer where it came from.

Perform the same routine for the SSH key corresponding to your official account with corresponding account on GitHub.

2. Setting up a configuration file to use easily co-ordinate among multiple accounts

Now to make life easy with multiple keys stored on a single computer, we will create a configuration file containing the details about the hostname which needs to be connected to and the account details with which to authenticate. Create a config file with the following command:

$ notepad config

 

And copy over the following details:

# Personal
Host personal
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_personalaccount

# Work
Host work
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_workaccount

One very important thing to note here is to provide the hostname as given in the sample configuration and the user as “git” in both the cases.

The next step will be to make sure that SSH agent is running on the machine. Run the following command on bash shell to make it run in the background:

$ eval $(ssh-agent -s) 

This will start the SSH agent of it is not running in the background and will allocate it a process identifier. Now we will add the SSH keys for personal and professional accounts to the agent by the following command:

$ ssh-add id_rsa_personalaccount

$ ssh-add id_rsa_workaccount

If you had provided passphrases while creating the SSH keys, then you will have to provide the corresponding passphrases before adding them to the agent.

In order to make sure that we have indeed added the identities, run the following command:

$ ssh-add –l

Now that we have the keys added in the SSH agent and have the configuration file set up, we are in a position to test the authentication by connecting to GitHub with credentials as described in the config file. Ru the following command:

$ ssh –T personal

The aforementioned command will make ssh module take the config file placed in the .ssh folder by default and use host information defined therein. The result of executing the commands should be something like below:

Hi parakh! You've successfully authenticated, but GitHub does not provide shell access.

Repeat the process for the work account.

3. Setting remotes correctly

Now that we are done setting up the keys and configuration file, and have tested the authentication, we will create a dummy repository and push the changes to it. On GitHub, create a dummy repository, say, with the name DummyRepo. This will be the repository which we will use to upload our changes to, and pull the changes from. Navigate to a suitable location on your hard drive and run the following commands to create a repository mapped to DummyRepo:

$ mkdir DummyRepo
$ git init
$ touch file1.txt
$ printf “This is my file” >; file.txt
$ git add .
$ git commit -m "first commit"
$ git remote add origin git@personal:UserNameOfPersonalAccountAtGitHub/DummyRepo.git
$ git push origin master

If all the setup has been done correctly, then the push of changes to DummyRepo will succeed. Now test the pull from the repository. Create a text file over at the GitHub repository and commit it. Now run the following commands:

$ git pull origin master

This should succeed in pulling the newly created file and commit.

Hope this article was helpful to you.