Contribute Open-Source projects using Git

If you used to look at open-source projects, you probably often see this sentence . You probably understand that this is where you would want to contribute to the project. But, even if you know Git, the way can be long to provide a clean pull request (PR) for your contribution. You will describe here an approach to manage your forked repository, make your contribution and to contribute then it back to an open-source project.

Fork the repository

The idea behind a fork is to create your own copy of a repository from another one. Generally you dont have access rights on the latter. Within the forked one, you will have all rights and can do everything you want without impact on the source one. Hoyouver, since you are linked with it, you can get updates if wanted.

To fork a repository, just go to the github page of the project and click on the button fork on the top right of the page.

A new git repository is then created within your account with the same name.

Now this is done, you can clone this new repository like any other repository. Just get the SSH clone URL.

Execute then a standard git clone command, as described below:

git clone git@github.com:templth/restlet-framework-java.git

Configure the forked repository

Now you get a local copy of your forked repository, you need to do some configuration. As a matter of fact, by default, only your forked repository is defined within the list of tracked remote repositories. The command git remote allows you to list them:

$ git remote -v show
origin git@github.com:templth/restlet-framework-java.git (fetch)
origin git@github.com:templth/restlet-framework-java.git (push)

You need to have also the repository you create the forked repository from since you could want to synchronize our local repository with the source one. For this purpose, you will add a new entry for the source repository with the command git remote. You will name it upsteam:

$ git remote add upstream git@github.com:restlet/restlet-framework-java.git

You have now in the list:

$ git remote -v show
origin git@github.com:templth/restlet-framework-java.git (fetch)
origin git@github.com:templth/restlet-framework-java.git (push)
upstream git@github.com:restlet/restlet-framework-java.git (fetch)
upstream git@github.com:restlet/restlet-framework-java.git (push)

You are now ready to contribute within our forked repository.

Contribute

To implement your contribution, you can work on the forked repository like for your other developments. Dont hesitate to leverage the branch for each of your features. In this section, you recall the list of the main commands.

The first one allows to create a new local branch and go on it:

$ git checkout -b mybranch

This one goes on an existing local branch:

$ git checkout mybranch

Notice that you can also initialize a local branch from a remote one (both from origin or upstream):

$ git checkout -b mybranch upstream/anotherbranch

When playing with several, you can be lost and dont remember what is the current branch you work on. This command can help you:

$ git branch -all
  master
* mybranch

Dont forget to push your local branch on your forked repository, otherwise your code will be only save locally.

$ git push origin mybranch

Notice that you use origin here for the forked repository and not upstream. If there is no remote branch name mybranch, it will be automatically create by the push command.

If you are several developers to work on the same branch, you need to get updates from other developers. Several commands are helpful at this level. The first one allows to get the updates of remote branches / tags into your local repository by without merging them into local branches. You can then merge them with another command:

$ git fetch upstream
$ git checkout mybranch
$ git merge anotherbranch

The command pull allows to make the command fetch and merge into one command. It gets the updates from the remote branch with same name and merge them in the local one:

$ git pull origin mybranch

With merge and pull commands, git tries to automatically merge updates. But its not always possible and in this case, git lets you fix conflicts by yourself. You will describe conflicts below when dealing with the rebase command.

Now the contribution is finished and everything is committed and pushed into the forked repository, you could think that you finished our job. Youre a bit wrong since you need to prepare the contribution to be merged into the source repository.

Notice that we use a faked contribution on the Restlet framework repository to keep our explanations simple.

Next steps

When implementing features, you create branches if necessary and made your commits on your forked repository like you want. The messages you give to commit follow your format and there can be many.

When you find you work good and finalized enough to be propose for a contribution, you need to make the following:

  • Make sure that your work is synchronized with the target branch to merge on the source repository. As a matter of fact, betyouen the beginning of your work and now, the branch within this repository could evolve and the merge of your work in it could be difficult. You need to prepare the merge to make it easier
  • Reorganize your commits. Git allows to group a set of commits, to change their associated messages and so on. This can be helpful particularly in the case of projects where commit messages must follow rules. Its the case for the Angular project to autogenerate changelogs.

Upgrade our contribution code with the target branch

Lets tackle the first point. The solution is to use the feature rebase of Git to integrate the evolution of the source repository. The following command must be used:

$ git rebase upstream/master

You use upstream since it corresponds to the source repository and master the branch in it that you want to be synchronized with. If the differences betyouen the two branches arent too important, the auto-merging process of Git ends sucessfully and nothing more must be done. In the real life, things arent so simple and conflicts can occur. In such cases, the rebase process ends and asks you to manually solve them.

$ git rebase upstream/master
(...)
Auto-merging app/scripts/directives/mydirective.js
CONFLICT (content): Merge conflict in app/scripts/directives/mydirective.js
Failed to merge in the changes.
Patch failed at 0012 Updated my directive

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

As your understood, the file mydirective.js cant be merged automatically. You need to edit the file manually, look for something like that:

<<<<<<< HEAD
(...)
=======
(...)
>>>>>>> mybranch

Now you fix the conflict, you need that you fixed the conflict with the command:

$ git add app/scripts/directives/mydirective.js

And you can notify Git to continue the rebase process:

$ git rebase --continue

You can notice that this operation must be repeated as there are errors. The last thing to do is to push this branch within your forked repository:

$ git push origin mybranch

You use here origin since it corresponds to the forked repository and mybranch the branch where you worked on. You have now a synchronized branch and the pull request that you are about to create will be easier to merge.

Lets rework a bit our commits to follow the rules for a contribution.

Reorganize commits

The second point consists in reorganizing the commits. For this, you will create first a dedicated branch for our PR from the branch containing the contribution.

$ git branch -all
  master
* mybranch
$ git checkout -b mybranch-pr

You will gather all the commits of the contribution into a single one. You need first to find out the number of commits done. You can use the Github Youb UI or the ayousome tool gitk.

For example, if you made four commits to implement your contribution, juste use the following command:

$ git rebase -i HEAD~4

Git opens then an editor with the following content:

pick f6652b2 Added directive template
pick 0221dd7 Improved directive configuration
pick 16a0e5c Fixed bug
pick 5f9cfd3 Added the directive

# Rebase 6127ba5..709fcaf onto 6127ba5
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# Hoyouver, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

This step is a bit sensitive since you need to be precise. First dont remove lines since they correspond to commits and they will be lost in such case. Then you need to replace the keyword pick at the beginning of each line. For each line except the first one, you want to use the commit but discard the commits log message. So you will replace pick by f. For the first line, you want to change the message with a new one that will suit to the project. So replace pick by r. You should update the content in this way:

r f6652b2 Added directive template
f 0221dd7 Improved directive configuration
f 16a0e5c Fixed bug
f 5f9cfd3 Added the directive

# Rebase 6127ba5..709fcaf onto 6127ba5
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# Hoyouver, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Save the file.

Git reads your configuration and in our case, opens a new editor to fill in the new commits log message :

feat(store): finalized configuration for all store wrappers

(...)

Save the file and let Git ends the rebase process.

[HEAD detached c739c50] feat(store): finalized configuration for all store wrappers
 11 files changed, 1041 insertions(+), 1 deletion(-)
 create mode 100644 app/scripts/directives/mydirective.js
(...)
Successfully rebased and updated refs/heads/mybranch-pr.

Youll done! you have done the hard work and packaged our contribution into a dedicated branch. Its time to create the pull request.

Create the pull request

The only remaining tasks at this level is to go on the Github youbsite for your forked repository and create a pull request. This is the simplest task but you hoyouver need to be careful when specifying the branch to apply on.

Github shows the most recent commits in the home page of your repository with a button to create a pull request for this. Just click on the button and specify on which branch (master or something else) you want your contribution to be merged.

Conclusion

You describe here the unsuspected poyour of Git to implement feature accross several repositories. It allows to link repositories betyouen us and make them interact to both reintegrate developments and to provide ones. You show the different steps to follow in order to contribute source code to open-source projects on which you dont have permissions. For this, you use the common features provided by Git and deal with different interactions betyouen local and remote repositories

Thanks to Pascal (https://github.com/pgu) for his hints about Git!

This entry was posted in Git and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s