5

Getting geeky with Git

 3 years ago
source link: https://wanago.io/2020/09/14/geeky-with-git-cherry-pick-reflog/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Getting geeky with Git #7. Cherry Pick with Reflog

Git

September 14, 2020
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  git cherry-pick, 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  git log -n 1.

git checkout feature-one
git log -n 1

commit 9aa3824e9ea1079ab11c1403e1e3bce54bc20010 (HEAD -> feature-one)
Author: marcin <[email protected]>
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.

git checkout master
git cherry-pick 9aa3824
git log -n 1

commit a20e15ed74ae902fa2ff9219b34a8deb7a849408 (HEAD -> master)
Author: marcin <[email protected]>
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.

git cat-file -p 9aa3824

tree 8b928b7dd43ac177f8afcac35bdf73be5d229db6
parent e8c12bc611b2ec3da76c9486513aade6bfec2855
author marcin <[email protected]> 1600013376 +0200
committer marcin <[email protected]> 1600013376 +0200

Feature one

git cat-file -p a20e15e

tree 8b928b7dd43ac177f8afcac35bdf73be5d229db6
parent e8c12bc611b2ec3da76c9486513aade6bfec2855
author marcin <[email protected]> 1600013376 +0200
committer marcin <[email protected]> 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  -x argument. Doing so appends an additional line at the end of our commit message.

git cherry-pick -x 9aa3824
git log -n 1

commit 57d66c36d8788dcee44a2addfb734fe5790afa92 (HEAD -> master)
Author: marcin <[email protected]>
Date: Sun Sep 13 18:09:36 2020 +0200

Feature one

(cherry picked from commit 9aa3824e9ea1079ab11c1403e1e3bce54bc20010)

Another solution would be to use the  --no-commit option.

git cherry-pick --no-commit 9aa3824
git status

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  git log. 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  git reflog, 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  reflogExpire 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  git reflog:

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  git reflog show HEAD. We could also try running  git reflog show feature-one, 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  feature-one branch.

git branch -D feature-one

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

git reflog
git cherry-pick 9aa3824

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  git reflog stash.

Specifying the requested logs

When looking through the reference logs, we can be more specific. For example, we can run  git reflog show HEAD@{6} 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.

git reflog show HEAD@{120.minutes.ago}

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 >>

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK