In a previous blog post I described a strategy for tracking an open-source project using the rebase & weld git operation that combines the advantages of rebase and merge operations. In this article I’m going to cover how to install and set up the git-upstream
tool, which implements that strategy. Examples are going refer to GitHub.
Installation
To get to the latest version of git-upstream
, I recommend installing from source. As of this writing, git-upstream
requires Python 3.9, not newer, so install it on your workstation using the method appropriate for your operating system (if you are not sure how, google for “install old version of Python on <your system>”). Then run the following commands:
git clone https://opendev.org/x/git-upstream.git cd git-upstream python3.9 -m pip install -r requirements.txt python3.9 -m pip install .
Goal
Let’s assume you want to develop a patch to the following project: https://github.com/argoproj/argo-rollouts
. You then want to deploy the patched software to your environment. After some time, you also want to import any future changes to the upstream project, taking advantage of the git upstream import
approach.
Repo Creation
GitHub has built-in buttons to fork a repository and to sync with upstream, but we’re not going to use them, because GitHub uses an ordinary merge or rebase, not the rebase & weld strategy. In addition, GitHub doesn’t allow to make a fork private or internal, even if you’re a user of GitHub Enterprise. So we’re going to use the command line to fork the repo.
I recommend that you use the same name for the repository as the original, but put it under a different organization. For example:https://github.com/argoproj/argo-rollouts
becomeshttps://github.com/akorzynski/argo-rollouts
.
Create the repository using the standard GUI, for example:
- in the personal version of GitHub, go to: https://github.com/new
- in GitHub Enterprise, use the following link, but replace MY_ORG with your org: https://github.com/organizations/MY_ORG/repositories/new
Don’t create a README
file or a license for this repository. All the files will be cloned from the original open-source repo. We want a completely empty repository for our purposes, as in the screenshot below:
Define the Environment
Define the following environment variables to correspond to your situation. With those variables defined, you will be able to copy/paste the commands from the upcoming sections, without having to modify them.
UPSTREAM_ORG=argoproj # source organization MY_ORG=akorzynski # destination organization REPO=argo-rollouts # name of repository
Copy Repository Contents
Run the following commands:
# Clone the repository git clone "https://github.com/$MY_ORG/$REPO" cd "$REPO" # Add the upstream repository as a new remote, # so that you can fetch the data from it git remote add upstream "https://github.com/$UPSTREAM_ORG/$REPO" # Fetch the data git fetch --all # Retrieve the head branch name HEAD_BRANCH="$(git remote show upstream | \ grep '^ *HEAD branch:' | \ grep --only-matching '[^ ]*$' )" # Push the head branch to your internal repository git push origin --set-upstream \ "upstream/$HEAD_BRANCH:refs/heads/$HEAD_BRANCH" # Push the data to your internal repository, # prefixing branches with 'upstream/' # (command based on git-upstream's documentation) git for-each-ref refs/remotes/upstream --format "%(refname:short)" | \ sed -e 's@\(upstream/\(.*\)\)$@\1:refs/heads/upstream/\2@' | \ xargs git push --tags origin # Checkout the head branch git checkout --track "origin/$HEAD_BRANCH"
Commit your change
Develop your change using your favorite editor. Then commit:
git commit -a -m "My change"
Import Upstream
When upstream changes, find the ref you want to import (usually the latest tag or the tip of the default branch). Then, assuming you want to import v1.2.3
, you can do so using the following command:
git upstream import v1.2.3
Import Upstream – Advanced
If you wish to change the patch queue before welding it to main, run the following:
# Define a variable with the Git ref to import GIT_REF="v1.2.3" # Create an import branch without the last "welding merge" git upstream import --no-merge --force --import-branch "import/$GIT_REF" "$GIT_REF" # Switch to the import branch git checkout "import/$GIT_REF" # Use the interactive rebase to change the patch queue as required git rebase --interactive "import/$GIT_REF-base" # Return to main git checkout main # Create the final "welding merge" git upstream import --finish --import-branch "import/$GIT_REF" "$GIT_REF"
Conclusion
We are done! You’re now tracking the repository using git-upstream.
Pingback: Beyond Merge and Rebase: The Upstream Import Approach in Git – DevOpsera