===== Overview ===== Typical workflow for contributing to a public GitHub project, demonstrating the use of branches and remotes. For this demonstration, I'll use a GitHub project I've been contributing to recently -- [[https://github.com/containers/libpod]] -- which is the source of the Docker lookalike ''podman'' command (among other things). ===== Getting a GitHub Account ===== Unsurprisingly, if you don't already have a GitHub account, get one -- examples that follow will refer to my GitHub account ''rpjday''. ===== Standard GitHub workflow ===== ==== Fork the project of interest ==== As a first step, over at GitHub, you need to "fork" the public project of interest -- in this case, the public project ''containers/libpod'' will be forked to a copy that I will own (also residing at GitHub), ''rpjday/libpod'', and here's the secret of the GitHub workflow. Once you create your personal fork of that public project ''containers/libpod'', you will //not// update that personal copy from the public version, nor will you pull from your personal fork; rather, your personal fork will be used as the //destination// to which you //push// branches with changes from your local machine that you will ask the ''containers/libpod'' maintainers to pull from via "pull requests". So go ahead and create that personal GitHub fork before going any further. ==== Clone your personal fork to your local machine ==== Once you've made that personal GitHub fork (agin, in my case, ''rpjday/libpod''), you need to make one more copy of the repository -- this one will be a clone of your personal fork to your //local// machine, which represents the working tree in which you create new branches and make local changes that you'll push to your GitHub fork for eventual incorporation into the public project, but here's the trick for clarity. Since you'll (shortly) be working with two remotes for this workflow, when you clone your personal fork, select a remote name other than the default of ''origin'' -- I'll use the remote name of ''rpjday'' to clearly identify that this is the remote corresponding to my personal fork over at GitHub, not the public project: $ mkdir podman/libpod $ cd podman/libpod $ git clone -o rpjday https://github.com/rpjday/libpod git Verify that everything here is up to date with your GitHub fork: $ cd git $ git status On branch master Your branch is up-to-date with 'rpjday/master'. nothing to commit, working tree clean $ In addition, you can verify that your local clone has one registered remote -- your personal GitHub fork with remote name ''rpjday'': $ git remote -v rpjday https://github.com/rpjday/libpod.git (fetch) rpjday https://github.com/rpjday/libpod.git (push) $ Again, keep in mind that this remote is where you will push your intended contributions to the public project. ==== Adding a remote for the public project ==== In addition to the remote for your personal fork, you need to register a //second// remote representing the public project, as this is where you'll //pull// new content from, including any of your pull requests that are accepted and merged into the public master branch. Again, for clarity, choose a remote name other than the default of ''origin'' -- I like to use the project name itself (in this case, ''libpod''): $ git remote add libpod https://github.com/containers/libpod $ You can now verify that your local clone has two registered remotes -- one for pulling new content added to the public project (''libpod''), and the second your personal remote for pushing your submitted changes (''rpjday''): $ git remote -v libpod https://github.com/containers/libpod (fetch) libpod https://github.com/containers/libpod (push) rpjday https://github.com/rpjday/libpod.git (fetch) rpjday https://github.com/rpjday/libpod.git (push) $ You can see this information in your ''.git/config'' file: $ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "rpjday"] url = https://github.com/rpjday/libpod.git fetch = +refs/heads/*:refs/remotes/rpjday/* [branch "master"] remote = rpjday merge = refs/heads/master [remote "libpod"] url = https://github.com/containers/libpod fetch = +refs/heads/*:refs/remotes/libpod/* $ ==== Keeping up to date with public content ==== Even as you're working on contributions to the public project, there is undoubtedly new content being merged into its ''master'' branch from others, and you probably want to keep up with that new content in case you need to rebase any of your work on top of it. First, you can, as often as you like, fetch from the upstream (public project) ''libpod'' remote into your remote tracking branch ''libpod/master'' with: $ git fetch libpod Occasionally, you might want to merge that new content into your local ''master'' branch in case you need to do any local rebasing: $ git checkout master $ git merge libpod/master Finally, whenever you update your local master branch, you should push it to your personal fork (which is how your personal fork at GitHub gets updated): $ git push rpjday master Do all of this on a regular basis to keep everything in sync. ===== Contributing changes ===== ==== The general steps in contributing changes ==== Contributing changes to the public GitHub project involves three steps: * Make and commit some //local changes// (preferably on a feature branch). * Push those changes (that branch) to your personal GitHub fork. * Over at GitHub, make a "pull request" to have the main project accept your changes on that branch. ==== Making some local changes ==== For each set of related changes, start a new feature branch with a distinct name that reflects what that branch is doing: $ git checkout -b rpjday/README_changes Switched to a new branch 'rpjday/README_changes' $ For our example, make some trivial changes to the top-level ''README.md'' file, and locally commit those changes on that branch: $ git commit -a -m "README.md: silly changes" [rpjday/README_changes b1a09348] README.md: silly changes 1 file changed, 4 insertions(+) $ ==== Pushing your work ==== You can continue adding and committing (related) work to this feature branch and, once you're satisfied, you can push that branch to your personal GitHub fork with: $ git push rpjday rpjday/README_changes ==== And over At GitHub ==== Provided you're logged into your account at GitHub, you'll suddenly see the appearance of a new branch ''rpjday/README_changes''. If you think it's ready to go, you can select "Compare & pull request" to examine and confirm that you want to hand that off to the main project. If all goes well and your change is accepted and committed (in this case, into the ''master'' branch), you will have to perform the two earlier steps to fetch and merge your changes into your local clone into the ''master'' branch: $ git fetch libpod $ git merge libpod/master In addition, if you have no further need of that local feature, you can delete it: $ git branch -d rpjday/README_changes ==== Adjusting your local branch ==== Given the possibility that your pull request might provoke some comments about possible improvements, you can make those local improvements, stage them and commit them on the same branch as before, then force push that branch to replace the older one: $ git push -f rpjday rpjday/README_changes