Most community/user developers have simple git goals, for example:
Changing the Linux kernel
defconfig to enable a driver
Bumping the version for a software or binary add-on package
Contributing to project documentation!
The goal of this page is to explain essential git workflow and terms. Git is mind-bendingly
complicated clever and there are hundreds of tricks to learn, but the basics are logical and quite simple to pick-up. One of the good things about git is; everyone is still learning! and everyone remembers what it was like to be a git n00b at the beginning. If you need to ask questions, ask them, as people are happy to coach.
The project maintains its primary git epository (repo) at https://github.com/LibreELEC/LibreELEC.tv/. The repo hosts the
master development branch where the next release takes shape, and stable release branches, e.g.
libreelec-9.0 that accumulate release-specific changes (commits) over time.
To self-build an image and test something you can simply
git clone our sources and start building, but if you want to maintain personal customisations and changes over time it's better to
fork our sources to your own git repo. Having your own fork makes the process of storing, sustaining, and sharing your changes easier. Forking is done online by visiting the LibreELEC repo and then pressing the fork button (top right of the screen). GitHub will make a copy of our sources under your own user account. Now instead of cloning OUR sources you
git clone a copy of YOUR sources to your local hard-drive.
git clone https://github.com/username/LibreELEC.tvcd LibreELEC.tv
Like ours, your fork will have a
master branch and release branches like
libreelec-9.2 which contain a sequence of commits. When you clone your repo to your local drive, git automatically creates references to your copy on GitHub which is the
origin of your local sources. To update your local branches with changes from our
upstream branches, we will define a
remote repo. You can add many remote(s), but we will add just one:
git remote add upstream https://github.com/LibreELEC/LibreELEC.tv.git
One of the most important principles of git is NEVER WORK IN THE MASTER BRANCH !!. To make a one-time change then build an image it's fine, but if you make changes in branches that we maintain (upstream) it becomes complicated to maintain your local (downstream) changes. It's best to keep your changes in a seperate "topic" branch. To create a new topic branch, you
checkout a new branch from the one you want to modify. For example, to modify the
libreelec-9.2 branch, we switch to it, then create the "mychanges" topic branch:
git checkout libreelec-9.2git checkout -b mychanges
In git-speak when you save your work you
commit changes to the working (topic) branch. The mantra for committing is "commit early, commit often" because it's always simple to combine (squash) commits together at a later stage, but hard to split a large commit into many smaller pieces. Commits can document changes to a single file or a collection of files, with a readable "commit message" that describes the changes. LibreELEC has a "house style" for commit messages:
tag: summary of changes made where
tag is the name of the package being changed or a functional area of the build system and
summary of changes made explains in brieft what the change does. It helps to make them descriptive so you can read the
git log later and understand which commit changed something. For example, if we enable an extra driver or module in the Linux kernel:
Make changes and save the file:
git add projects/Generic/linux/linux.x86_64.confgit commit -m "linux: enable RTL8822CE wireless driver"
Most users will make changes first, then build and test them, then
commit the changes.
Over time staff and users contribute chaanges to our
upstream branches and your local fork falls behind. To update your "mychanges" topic branch you must first commit any unsaved changes, then switch back to the branch it was based upon, e.g. the
git checkout libreelec-9.2
Next we need to
fetch the latest changes from the
upstream branch to our local cache, then reset the local copy of the branch to match the upstream one:
git fetch upstream libreelec-9.2git reset --hard upstream/libreelec-9.2
Now the local
libreelec-9.2 branch contains the latest changes we can change back to our
mychanges topic branch and
rebase the branch. Rebasing re-applies the single or handful of changes that you made (and committed) to the branch you are rebasing against, e.g.
git checkout mychangesgit rebase libreelec-9.2
If you look at the log of commits you will see the
mychanges branch matches the upstream
libreelec-9.2 branch, with your local changes applied on top.
git log --pretty=oneline
This is the git trick that everyone needs to learn. It is often the "Eureka!" moment that moves a user from novice to normal. Interactive rebasing allows you to (re)edit, remove, and reorder the sequence of commits in the branch. The reasons you will want/need to do this are dropping a change, grouping commits together so the sequence is easier to understand, to edit (reword) one or more commit messages, or to combine (squash or fixup) multiple small changes into a single larger commit. Interactive rebasing is done to a specific number of commits measured from the top (HEAD) of the branch, e.g. to rebase the last five changes:
git rebase -i HEAD~5
The list of commits will open in a text editor, e.g. "nano" and you can cut (ctrl+k) and paste (ctrl+u) the commits to reorder them. For example, lets say we have the following list which shows which commits are being pick(ed) to the branch:
pick 73d9356c36 fix typos in file Apick 29de6de78b add new file Bpick be444ce9e7 more typos in Apick cf7149bc7e more typos in Apick 750b824c04 changes to file B
It would be more logical to group all the
file A and
file B commits like this:
pick 73d9356c36 fix typos in file Apick be444ce9e7 more typos in Apick cf7149bc7e more typos in Apick 29de6de78b add new file Bpick 750b824c04 changes to file B
The next logical step might be to combine the changes so there are only two commits; one for changes to file A and one for changes to file B. This is done by changing
fixup which combine commits. In the sequence below, the first commit is at the top, and we want to combine the next two minor changes into it. If you
squashcommits you will be asked to edit the commit messages (choose which of the commits should be used for the message) while
fixup combines the lower (second) commit into the upper (first) commit and uses the first commit message without prompting. For example:
pick 73d9356c36 fix typos in file Afixup be444ce9e7 more typos in Afixup cf7149bc7e more typos in Apick 29de6de78b add new file Bfixup 750b824c04 changes to file B
Once you save (ctrl+o) and exit edit mode (ctrl+x) the reordering and squash/fixup changes are applied, leaving you with two commits for "fix typos in file A" and "add new file B" in the branch. It is a good habit to squash and rebase frequently to keep your changes logically grouped and in a minimum number of commits. This is important if you want to send your changes upstream in a "pull request" to our repo. Project staff want to review a small and logical set of changes, not the monster list of nip-tuck changes you needed to figure out what changes were needed.
Another good habit is
pushing your local changes back to your
origin branch on GitHub. If something gets messed up in your local branch (and when you are starting out with git, stuff will get messed up!) you can always reset your local branch to the previous known-good state stored on GitHub. Pushing the changes is simple:
git push origin mychanges
If you have rebased the
mychanges branch the branch history will have different references and a force-push will be needed:
git push -f origin mychanges
There are also no restrictions on the number of branches you can make. So fork branches and push them upstream. It's better to fork and store changes and then tidy things up occasionally than lose changes and have to repeat/reinvent them.
If your changes might benefit the project and you'd like to see them included
upstream in our repo so you don't need to maintain them in your
downstream fork, you'll need to submit a
pull request. This is done in the GitHub interface. You need to push your changes to your origin branch, and then when you view the code on GitHub, select the "pull request" button. You'll be prompted to select the repo/branch to send the request to, e.g.
LibreELEC.tv/master and you'll give the request a name and description. If your pull request modifies code, please include a good description of the problem that your changes solve or what the feature is that you added, and (important!) the testing you conducted. So called "blind requests" where there is no evidence of testing and no meaningful explanation of the change, are rarely merged (or merged quickly) into our codebase.
Note: All functional changes should be submitted to the
master branch first, even if the target for your changes is the current stable release branch. This way the branches remain in sync, and we don't have orphaned features.