Git for TVH Dummies¶
As I get quite a few people asking me what the right way to work with git is, in particular in relation to submitting patches upstream, I thought it might be a good idea to write a quick wiki entry to help with some common tasks.
I'm also fairly new to git, I'd only touched it occasionally before getting involved in Tvheadend, but I've now been using it solidly for about a year. So while I'm by no means an expert, I'll at least point out some of the things I've learnt over that year.
Tvheadend hosts its primary repository on github, this is a useful community site as not only does it provide basic git hosting but also some useful tools for managing the project. This includes the ability for users to create public forks, submit Pull Requests and to comment on commits and other things.
The main project page can be found at https://github.com/tvheadend/tvheadend.
If you don't intend to actively contribute to the development you can simply clone the repository for the purpose of Building and the rest of this article is probably not relevant. However should you later decide you do want to push stuff back upstream then there are some useful hints below.
The first thing you should do if you intend to actively develop and push features/fixes upstream is to create a public fork on github (you can use other services, but it won't fit as well with the general project workflow and the Team may get grumpy!).
To create the fork simply click on the fork icon:
This will create a new personal fork on github, which will allow you to publish your own changes etc.
You will also need to add an SSH key to your account to be able to push changes to your fork. If you're unsure how to do this please visit the github ssh help page.
Once that is all done you should create a clone of your repository using the SSH access URL (this will give you push access):
git clone [email protected]:USERNAME/tvheadend.git
Syncing with upstream¶
If you want to keep up to date with upstream changes (in the main repository) then you should add a remote (typically called upstream):
git remote add upstream git://github.com/tvheadend/tvheadend.git
To pull the latest state of that repository:
git fetch upstream
And to merge changes into your local branch:
git merge upstream/BRANCH
Fixing upstream clone¶
If you have already cloned the upstream (tvheadend/tvheadend.git) repository and now want to create you're own github fork, while still retaining your local repository (possibly with commits you've already made). First create the github fork as above, and instead of cloning that repository do the following within your existing local repository:
git remote rename origin upstream git remote add origin SSH_URL git fetch origin git branch --set-upstream master origin/master
It is highly recommended that when you're looking to make changes, especially those you eventually want to submit upstream, that you create a branch.
There are several ways to create branches within git, but the two most common that I use are:
git branch BRANCHNAME
git checkout -b BRANCHNAME
This will both create a new branch (called BRANCHNAME) from the currently HEAD. The only difference is the second command will also switch you to the newly created branch.
If at any time you want to checkout another (existing) branch, do the following:
git checkout BRANCHNAME
Finally should you want to create a local branch from an existing branch on another fork (such as the upstream tvheadend fork), you can do the following:
git checkout -t REMOTENAME/BRANCHNAME
This will create a new local branch, with the same name as the remote branch, set it up to track (this means git will among other things tell you the status of your branch relative to the tracked version) and switch to the new branch.
To publish this branch to your public repository, as always with git there are a number of different ways of doing this. However for simplicity I usually do the following:
git push -u origin HEAD
This will publish a new branch to your public repository, using the same name as the local branch. It will also set the local branch to track the remote one.
If you need to send subsequent updates, you can simply do the following:
git push origin HEAD
Note: Both of the above commands assume that you've already checked out the branch you want to publish.
Eventually you've hopefully created some wonderful new feature or fixed some horrendous bug and you want to share that with the rest of the project. The way this is done is by creating a Pull Request (PR for short).
A PR is basically a way of saying "hey I've got this cool code, please include it upstream". The development team (or whoever you submit the PR to) can then review your commit, will possibly make comments and ask for changes. And hopefully, if the change is a good one, it will eventually be pulled into the upstream fork and the rest of the world will benefit.
To begin the PR process, you should hopefully already have a branch that you intend to submit. If not create one now, it'll save you hassle later. The reason being that when you submit a PR you're basically saying pull from this branch, so any commits you add to that branch will automatically be added to the PR. And then make sure this has been published to your public fork.
If you visit your github project page, you should now see something like this:
Click the "Pull Request" button, fill in the form and hit "Send Pull Request". And that's it, code submitted and ready for review.
One thing that you should really think about doing before submitting a PR is to clean up the branch, if you don't you'll mostly likely be asked to when you do submit it.
The bare minimum you should look to do is rebase your commits on top of the current upstream master. This ensures that the code will cleanly merge and that no conflicts exist. It will also make the commit history cleaner and easier to review.
To do the rebasing, run the following command:
git rebase upstream/master
You could also squash some commits, this can be especially useful if the branch is littered with lots of commits as you to and fro your way to a solution (mine certainly are usually like that!). The way I usually do this is to use the interactive rebase tool:
git rebase -i upstream/master
This will present you with a list of your commits, something like:
pick 46f2170 update readme date # Rebase 2e25766..46f2170 onto 2e25766 # Some helpful info...
From here you can reorder commits, by simply moving the lines around, squash commits (change pick to fixup or f), remove commits completely (delete the line) or simply change the commit message (change pick to reword or r). Save the file and git will start to apply the changes.
If the process encounters problems, such as conflicts, then you will need to intervene. The conflicting files will need to be edited to resolve the conflicts, then added back into the commit and finally the rebase continued:
edit FILE(s) - manually resolve conflicts git add FILE(s) git rebase --continue
How you go about resolving conflicts is down to you.
It's also worth noting that when you rebase things you're changing history and this may cause problems when trying to push to a remote (if the commits you've modified were already upstream). To resolve this you may be forced to forcefully push the updates, using:
git push -f origin HEAD
Should you want to make changes, or be asked by the upstream Team, then you will need to first make these in the local copy of the branch used for the PR. You then simply publish those changes upstream:
Remote's were briefly mentioned in the earlier sections without really making it clear what they are. In essence, a remote is simply another git repository/fork that is not the local one. Because of the distributed nature of git, you can easily interact with numerous forks at the same time. Pulling change from one and pushing to another etc.
Normally when working with github (and other hosted solutions) you will normally have 1 or 2 remotes by default:
origin - This is the hosted/public copy that your local fork is notionally attached to. upstream - This is typically the hosted/public copy of the parent of origin.
There is nothing particularly special about the names origin and upstream, they are simply convention. But I'd recommend sticking with that convention.
You can also add any other repository you like to your list of remotes and that will give you the ability to pull and push (depending on your access) to those repositories. For example I've currently got this horrible mess of remotes:
adamsutton [email protected]:adamsutton/tvheadend.git (fetch) adamsutton [email protected]:adamsutton/tvheadend.git (push) andyb2000 git://github.com/andyb2000/tvheadend.git (fetch) andyb2000 git://github.com/andyb2000/tvheadend.git (push) bombadil [email protected]:Workspace/xbmc/tvheadend/tvheadend (fetch) bombadil [email protected]:Workspace/xbmc/tvheadend/tvheadend (push) btbn git://github.com/BtbN/tvheadend.git (fetch) btbn git://github.com/BtbN/tvheadend.git (push) krka git://github.com/krka/tvheadend.git (fetch) krka git://github.com/krka/tvheadend.git (push) manio git://github.com/manio/tvheadend.git (fetch) manio git://github.com/manio/tvheadend.git (push) origin [email protected]:tvheadend/tvheadend.git (fetch) origin [email protected]:tvheadend/tvheadend.git (push) tornblom https://github.com/john-tornblom/tvheadend.git (fetch) tornblom https://github.com/john-tornblom/tvheadend.git (push)
It includes my own github fork, the fork on my TVH server (bombadil) and several user repositories I've interacted with recently. Its worth noting that I have no upstream. This is because this is my fork of the tvheadend/tvheadend.git repository, which as the root of all the forks does not itself have a parent.
To add a new remote simply do the following:
git remote add REMOTENAME REMOTEURL
You can then sync you local copy of that repository (yes you have all the info for each repository stored locally, though due to the way git is built most of it will already exist on your disk and not require lots of extra space):
git fetch REMOTENAME
If you want to checkout a remote branch, maybe to have a quick play, you can simply do:
git checkout REMOTENAME/BRANCHNAME
This will created what is known as a "detached HEAD", it means that its not part of a branch and so you shouldn't make commits (as they'll be lost when you switch to another branch). If you want to create a new branch from the detached state, you can simply do:
git checkout -b BRANCHNAME
Tips and Tricks¶
Just a random collection of git commands that might help with using git, finding info, etc...
git log --oneline --decorate
I alias this as gl as it provide a very nice compact git log output.
git shortlog -nse
Useful to find out who has contributed to TVH (some of this can be seen on github).