1. Overview
Git is a popular version control system that helps us track modifications to a set of files. It also provides a way to make commits in a collaborative fashion, which is currently a requirement for most organizations.
When working with git, we may have encountered a situation when one repository contains a stable version of a project and another one has the code for a new feature. In this scenario, the best step to take is to merge these repositories by copying the commits from one to another.
In this tutorial, we’ll take a look at the process that we can use to copy git commits from one repo to another in Linux.
2. Example Repositories
In this process, we’ll be working with two repositories named source and destination, where we’ll move all commits from the source repository to the destination repository.
Firstly, let’s take a look at the entire commit history of the source repository:
$ git log --oneline
467dc74 (HEAD -> main, origin/main, origin/HEAD) Add Sublime Text
123c40c Add CONTRIBUTING.md
129e55f Add LICENSE.md
930bd17 Add Visual Studio Code
2d69ae6 Add an online course by Andrei Neagoie
Above, we’ve used the git log command to list all the commits in the source repository. Additionally, we’ve utilized the –oneline option to restrict every entry to a single line.
Secondly, let’s check the first five commits in the destination repository:
$ git log --oneline
4c10ef0 (HEAD -> main, origin/main, origin/HEAD) Update README.md
ef5563e Update CODE_OF_CONDUCT.md
6756f52 Update LICENSE.md
4695571 Add FOSSASIA Codeheat
27f8d35 Update CONTRIBUTING.md
The alphanumeric string that we see on each line is known as a commit hash, which is a unique identifier assigned to every new commit.
The commits that we’ll move to this repository will go to the top of the latest commit, i.e., the one with the commit hash of 4c10ef0.
3. Add source as a Remote Reference
A remote reference (or a remote repository) for a local repository indicates a repository that is hosted on a particular URL and is connected to the local one. After connecting the two, we can push changes to and fetch changes from the remote repository to the local one.
If we want to import commits to the destination repository, we have to access the commit log of source. To do that, we can add source as a remote reference for destination.
Firstly, we navigate to the local directory of the destination repository:
$ cd destination
Now, let’s add the source repository as a remote reference:
$ git remote add source https://github.com/user/source.git
However, we still can’t access the commit log of source despite it being connected to destination. The reason is that we haven’t yet updated the remote reference to fetch the commits from the source repository. To do that, we’ll use the git remote update command:
$ git remote update
Now, let’s check the commit history:
$ git log --all --oneline
4c10ef0 (HEAD -> main, origin/main, origin/HEAD) Update README.md
467dc74 (upstream/main) Add Sublime Text
ef5563e Update CODE_OF_CONDUCT.md
6756f52 Update LICENSE.md
123c40c Add CONTRIBUTING.md
129e55f Add LICENSE.md
930bd17 Add Visual Studio Code
2d69ae6 Add an online course by Andrei Neagoie
4695571 Add FOSSASIA Codeheat
27f8d35 Update CONTRIBUTING.md
Evidently, all the commits from the source repository are now accessible. However, none of them belong to the destination repository yet. We’ve used the –all option with the git log command to list all commits present in the remote repository source along with the ones in the local repository destination.
At this point, we’ve only connected the repositories, without copying any commits.
4. Copy Commits Using git cherry-pick
Now, that we have access to both linked repositories, we can move on to the next step, where we’ll copy commits from source to destination.
In particular, the git cherry-pick command enables us to pick a commit from one branch and append it to another:
$ git cherry-pick 07c1df2
The alphanumeric string that we’ve used after the command signifies the commit hash of the commit that we want to pick.
Now, before we use cherry-pick, we should make sure that the working tree is clean. In other words, we have to verify that the Git repository that contains all the files and directories has no uncommitted changes after the most recent commit.
So let’s check the current status using the git status command:
$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
As we can see above, the working tree is clean.
Now, we can use the git cherry-pick command to either manually pick individual commits or import all commits at once.
4.1. Manually Picking Individual Commits
Firstly, we’ll go through the manual way of cherry-picking commits. In this case, we’ll import the commit with the commit hash of 467dc74 from the source repository:
$ git cherry-pick 467dc74
[main e065671] Add Sublime Text
Date: Thu Jul 27 21:41:41 2023 +0530
1 file changed, 5 insertions(+), 2 deletions(-)
Evidently, a new commit has been made in the destination repository. However, in addition to the commit hash, the new commit has extracted everything, including the commit message and the contents, from the commit that we picked.
Now, let’s check the commit log of only the destination repository to verify if the commit is present:
$ git log --oneline
e065671 (HEAD -> main) Add Sublime Text
4c10ef0 (origin/main, origin/HEAD) Update README.md
ef5563e Update CODE_OF_CONDUCT.md
6756f52 Update LICENSE.md
4695571 Add FOSSASIA Codeheat
27f8d35 Update CONTRIBUTING.md
As we can see above, the commit with the message Add Sublime Text is present. It means that we’ve successfully moved it to the destination repository.
4.2. Import All Commits at Once
If we want to import all commits, then using the above method would be time-consuming. So, let’s use git cherry-pick in another way:
$ git cherry-pick b283733^..2d69ae6
[main df82a47] Add an online course by Andrei Neagoie
Date: Thu Jul 27 23:32:18 2023 +0530
1 file changed, 8 insertions(+), 3 deletions(-)
[main 27b5169] Add Visual Studio Code
Date: Thu Jul 27 21:32:26 2023 +0530
1 file changed, 6 insertions(+), 3 deletions(-)
[main 1c4702f] Add LICENSE.md
Date: Thu Jul 27 21:32:34 2023 +0530
1 file changed, 9 insertions(+), 2 deletions(-)
[main 8fb569b] Add CONTRIBUTING.md
Date: Thu Jul 27 21:32:42 2023 +0530
1 file changed, 8 insertions(+), 3 deletions(-)
[main 5847b88] Add Sublime Text
Date: Thu Jul 27 21:32:50 2023 +0530
1 file changed, 5 insertions(+), 2 deletions(-)
Visibly, a new commit is made for each commit that is imported from source to destination.
Above, we’ve entered a range (..), in which we’ve included the commit hash of the initial commit and the commit hash of the latest commit in the source repository. Moreover, the caret (^) operator enables us to include the starting commit. If we hadn’t used it, the operation would’ve started with the commit that came after the initial commit.
Now, let’s check the commit history of the destination repository and verify if it contains all the commits that we imported:
$ git log --oneline
5847b88 (HEAD -> main) Add Sublime Text
8fb569b Add CONTRIBUTING.md
1c4702f Add LICENSE.md
27b5169 Add Visual Studio Code
df82a47 Add an online course by Andrei Neagoie
4c10ef0 (origin/main, origin/HEAD) Update README.md
ef5563e Update CODE_OF_CONDUCT.md
6756f52 Update LICENSE.md
4695571 Add FOSSASIA Codeheat
27f8d35 Update CONTRIBUTING.md
Evidently, all the commits from the source repository have been copied to the destination repository.
5. Conclusion
In this article, we learned how to copy git commits from one repository to another in Linux. Notably, we might need to use a series of commands in this process, concluding with the git cherry-pick command that enables us to accomplish the main objective of the process, i.e., moving commits from one repository to another.