Getting geeky with Git #7. Cherry Pick with Reflog

Git

This entry is part 7 of 8 in the Getting geeky with Git

So far, we’ve covered a few different tools useful when rewriting the Git history, such as merging and rebasing. In this article, we go more in-depth and try cherry-picking. While doing so, we also learn how the reflog functionality works.

Git Cherry Pick

When we rebase or merge, we usually get all of the content of a branch. This might not always be the desired outcome. For example, there might be a case when we need to patch the production with some content of another branch. Although we would prefer to merge the work more gracefully, some situations might require us to apply some hotfixes. It is always a good thing to have the above possibility.

With  , we can choose a commit from one branch and apply it to another one. To do so, we need to provide Git with the hash of a commit.

If you want to know how Git creates hashes from commits, check out Getting geeky with Git #2. Building blocks of a commit

To get the most recent commit, let’s use  .

commit 9aa3824e9ea1079ab11c1403e1e3bce54bc20010 (HEAD -> feature-one)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun Sep 13 18:09:36 2020 +0200

Feature one

We don’t need to provide a full hash, since it is a lot of characters. Usually, seven or eight characters would be enough to identify a commit.

commit a20e15ed74ae902fa2ff9219b34a8deb7a849408 (HEAD -> master)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun Sep 13 18:09:36 2020 +0200

Feature one

The drawbacks of using cherry-pick

Doing the above moved the changes associated with the chosen commit into the master branch. We can notice a significant thing above. When we cherry-picked the above commit, Git created an entirely new object with a new SHA identifier. Let’s look even closer into the above commits.

tree 8b928b7dd43ac177f8afcac35bdf73be5d229db6
parent e8c12bc611b2ec3da76c9486513aade6bfec2855
author marcin <wanago.marcin@gmail.com> 1600013376 +0200
committer marcin <wanago.marcin@gmail.com> 1600013376 +0200

Feature one

tree 8b928b7dd43ac177f8afcac35bdf73be5d229db6
parent e8c12bc611b2ec3da76c9486513aade6bfec2855
author marcin <wanago.marcin@gmail.com> 1600013376 +0200
committer marcin <wanago.marcin@gmail.com> 1600013957 +0200

Feature one

Even though the above commits have the same contents when it comes to the changes, the commit object differs a bit.

When we look into the committer part of the above commits, we can spot a small difference. At the end of the line, there is a Unix timestamp with a timezone. Since we’ve performed the cherry-pick after the actual commit, it differs.

Since Git takes the timezone into account when generating the SHA hash, the newly generated hash is different than the original. All of the above might lead to having duplicates of commits. This does not help in keeping our history straightforward.

Making our cherry-picks more apparent

To deal with the above issue, we can use the   argument. Doing so appends an additional line at the end of our commit message.

commit 57d66c36d8788dcee44a2addfb734fe5790afa92 (HEAD -> master)
Author: marcin <wanago.marcin@gmail.com>
Date: Sun Sep 13 18:09:36 2020 +0200

Feature one

(cherry picked from commit 9aa3824e9ea1079ab11c1403e1e3bce54bc20010)

Another solution would be to use the   option.

On branch master
Changes to be committed:
(use “git restore –staged <file>…” to unstage)
modified: index.js

Doing that prevents Git from automatically committing the changes. Now, we can create a commit ourselves with a commit message of our choosing. It might prove to be especially useful if we want to cherry-pick multiple commits at once.

Git Reflog

In this series, we’ve often used  . It shows us the current HEAD and its ancestors.

In the third part of this series we’ve learned that HEAD is a pointer to a commit that our repository is checked out on

With  , we don’t traverse through the HEAD ancestors. It is a list that we can view to see the commits that the HEAD pointed to. Git stores it locally, so it is not included when pushing and pulling from a remote repository.

The expiry time for the reflog entries can be specified through the   option in the configuration. It defaults to 90 days. After this period, git removes entries that are not reachable otherwise

The most basic usage is through running  :

e8c12bc (HEAD -> master) HEAD@{0}: reset: moving to HEAD~1
57d66c3 HEAD@{1}: cherry-pick: Feature one
e8c12bc (HEAD -> master) HEAD@{2}: reset: moving to HEAD
e8c12bc (HEAD -> master) HEAD@{3}: reset: moving to HEAD~1
a20e15e HEAD@{4}: cherry-pick: Feature one
e8c12bc (HEAD -> master) HEAD@{5}: checkout: moving from feature-one to master
9aa3824 (feature-one) HEAD@{6}: commit: Feature one
e8c12bc (HEAD -> master) HEAD@{7}: checkout: moving from master to feature-one
e8c12bc (HEAD -> master) HEAD@{8}: commit (initial): Initial commit

The above is a shortcut for  . We could also try running  , for example.

We can take advantage of that and see commits that we would not be able to otherwise. Imagine that we would have accidentally removed the   branch.

Even though that happened, we can still get the hash of the commit that we might want to cherry-pick.

The above might help us in some nasty situations. Git even keeps a record of the work that we stash. We can access it through  .

Specifying the requested logs

When looking through the reference logs, we can be more specific. For example, we can run   to see where the HEAD pointed six moves ago.

9aa3824 HEAD@{6}: commit: Feature one
e8c12bc (HEAD -> master) HEAD@{7}: checkout: moving from master to feature-one
e8c12bc (HEAD -> master) HEAD@{8}: commit (initial): Initial commit

All entries in the reflog have timestamps attached. Thanks to that, we can filter them by time.

a20e15e HEAD@{Sun Sep 13 18:19:17 2020 +0200}: cherry-pick: Feature one
e8c12bc (HEAD -> master) HEAD@{Sun Sep 13 18:18:50 2020 +0200}: checkout: moving from feature-one to master
9aa3824 HEAD@{Sun Sep 13 18:09:36 2020 +0200}: commit: Feature one
e8c12bc (HEAD -> master) HEAD@{Sun Sep 13 18:08:49 2020 +0200}: checkout: moving from master to feature-one
e8c12bc (HEAD -> master) HEAD@{Sun Sep 13 18:07:25 2020 +0200}: commit (initial): Initial commit

There are more ways to specify the time when using the reflog. For a full list, check out the documentation.

Summary

Today we’ve learned a few new tools that might come in handy in various situations. Although we need to remember that using cherry-pick might have some negative effect on the readability of our history. When used wisely, it can become a useful command – especially when combined with the use of the reflog. Surely, it is good to be aware of both when the right moment comes.

Series Navigation<< Getting geeky with Git #6. Interactive RebaseGetting geeky with Git #8. Improving our debugging flow with Bisect and Worktree >>
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments