Home Pauline Howto Articles Uniquely NZ Small Firms Search
An Introduction to Git
Applications in Cinnamon Applet Development

This is a recent page complementing the existing Git 'Kernel Patching and Cinnamon' page. It was primarily intended to help others who want to make use of my Cinnamon Applet Development on GitHub, contribute to that development. It has been extended for those who want to set up their own Git repository for applet development or other such software purposes and make use of GitHUb. It now has an introduction to Git, its advantages, enough explanation of how it works and its basic commands to be able to use. There is a comprehensive list of references I go back to time and time again.

An Introduction to Git - what is it and why use it?

Git is a very sophisticated and versatile distributed version control system where every folder with a Git repository on every computer becomes a full-fledged independent repository with complete history and full version-tracking capabilities, without requiring continuous online access or a central server for its use. Most programmers, developers or whatever one likes to call them, eventually realises that they need some way of keeping a track of what they have done and the difficulties compound rapidly when several people are involved and become impossible with a large team without some form of control system.

I started to use Git because I have written several applets and I believe once one has made software available one has a responsibility to maintain it properly and make sure that future continuity is guarantied - there are too many applets where there have been no updates for 3 or 4 years and no easy route for anyone else to take over their updating as Cinnamon changes. By use of Git and GitHub (a free, for public use, central repository service) as a remote server others can see, clone and fork my work and even send me patches. It also makes it much easier to locate and fix any bugs.

I must starting by saying that using Git is not easy and requires use of the terminal/command line but will explain why it is so useful that it is worth the learning curve. Firstly a major advantage over other version control systems is everything is local, you can work for long periods and then push the details to a remote server or pull updates from it once and at your convenience. The second major advantage is that there is a simple mechanism to break away from the main development path (usually known as the master) to try things out simultaneously in many different areas and them bring them all back together through branching and merging. In fact many projects have a stable and development branches always running in parallel with the development branch periodically merged into the stable branch. In fact this can be done by a large team all working in parallel yet able to bring their work together. One can start a new branch from the current point (referred to as the HEAD) or from points in the past and then switch between branches very easily and commit a series of changes on each branch. Every time you change (checkout) branch your working directory reflects whats in that branch and its history. If you are not successful you can just delete the branch or if you have solved the problem you can merge it back along with changes pulled from the server and changes in other branches. This is all very fine in theory but what happens if the same file has been changed in different places? The system detects that and forces you to resolve the differences. In its most basic form Git marks the conflicts within the files so you can edit them. It also allows you to use meld or other GUI utilities to help by showing you the files side by side and move, delete or otherwise resolve by editing the differences. I have marked some of the terms orGit commands you need to understand in bold. In fact the terms above are almost all the basic you will need to know, only add, status, diff and help are missing from the essential command list.

There are some GUI interfaces to use Git but you really do need to understand Git by running it from the terminal before using them. The gitk visualiser is however very helpful, if not essential. But before we start to look at the important commands we need a basic understanding of the principles behind how Git works. I took a long time to use Git effectively and that was because I do not think I really understood the fundamentals of Git.

But to use Git it is essential to really understand more of the way it works. Firstly Git has a working directory where you edit files and a repository where a series of snapshots of the working directory are stored very efficiently. Note we are talking about snapshots not differences. You can load a snapshot into your working area by a checkout (as in supermarket!) To start with you probably have the simplest case with a single path (branch) by default called master and you are checked out at its HEAD. When you have made a series of edits on the files in your working area you need to take another snapshot and add it into the Git repository - this is called a commit and once that is done it is very difficult to lose data especially if your local repository is connected to a remote repository on a different machine. Git is also very efficient, for example the complete Git repository for the linux kernel back to 2005 when it was switched into Git is only 2 Gbytes and that it has every commit (snapshot) made since then (~450,000 of them). I suspect there are few larger and more complex projects.

Security is guaranteed as each snapshot (commit) is identified by it's checksum. The mechanism that Git uses for this checksum is called a SHA1 hash. This is a 40-character string composed of hexadecimal characters (0-9 and a-f) and calculated from the contents of a file or directory structure in Git. This is a unique identifier which also confirms that the contents have not been changed. These checksums are also the fundamental way you identify a particular snapshot to, for example, return to and reload your working area (checkout) or start a branch from. Each commit also has information attached to it including its parent(s), a message and the name and email of who made the commit.

The above are the most vital things you have to understand but are a simplification as the adding of the snapshot to the repository is actually two steps, the first is that individual files are added to a staging area and then those in the staging area are in the list to be committed. This unique “staging area” concept does allow you to determine exactly which changes shall be included in your next commits, even down to single lines. It is possible to modify the file further but the modifications will not be in the next commit unless it is restaged. Once a snapshot has been committed to the repository is very difficult to change. I have so far never needed to separately stage files and use a simplified command which adds and commits all changed files and aappends the message. Even so you must be aware that it is actually two steps and eventually you may need the flexibility. You should note that many people call the staging area the index.

So far I have only been covering local activity and have ignored the fact that in most cases you will be working collaboratively or making your work openly available through a remote Git repository which you will push your commits to and others will pull or fetch the changes back to your machine to merge into their work, likewise you may pull from their or other centralised repositories. So it is time to look at how you might want to use Git, in order of complexity.

  1. Take an existing 'project folder' such as an applet you are developing and add a Git repository to it so you have an audit trail for your changes and backup for mistakes.
  2. Combine the above with a hosted Git service like Github so you can make the work available to others and you can work on several machines as well as having a remote backup. This is what I have done with the applets.
  3. Clone a copy of an existing repository to make modifications for your own purposes, possibly adding patches which have not been included in the master at that time or testing patches with the intention of providing feedback or to get round problems. This is what I did to test and incorporate a patch to the kernel I was using on the Helios to get round some problems with the Skylake architecture used without waiting 6 months it to reach a kernel I could use.
  4. Fork an existing project repository so you can contribute to a major project such as Cinnamon. Cinnamon has close to 1 million lines of code, over 5000 commits, 122 releases, 373 forks and 143 contributors. Only a handful of people can make changes to the master, in the case of Cinnamon 95% of commits come from just two people - it is the only way to keep control. In the case of the Linux kernel almost everything is finally approved by Linus Torvalds himself. A fork starts as a copy of the main repository on the server which is then cloned to your machine after which it can be updated with your contributions. You then create a pull request which, if and when approved, can be pulled into the master and merged. This allows total control over the process.

At this point I am going to be concentrating on 3. and 4 as this is mainly about your access to my repository and I will come back to 1 and 2 at the end as that is where you may wish to make your work available to others.

Reference Information

I have been looking for a good place to add some reference information and this seems as good a place as any before we get on to the real subject of this document as the information I can give in a short article is only a small but relevant part of the whole Git story. I will also be refering to some of the documents here several times in the text that follows.

We can now get to the main business of this document.

Using Git to manage Applet development

I had several applets already published before I decided it was time to bring them under proper control by using Git. They were already sub folders within an existing folder and I turned the whole into a Git repository ( I will come to details of how latter) - this was kept entirely local to start with and for the period where I was trying to put earlier versions together to form a history to start everything going. Once I had worked back through some critical parts of the development I added a remote repository one of of the Git repositories hosting services. I had already working with GitHub when I was looking at Cinnamon. Github is the single largest host for Git repositories, and is the central point of collaboration for millions of developers and projects. A large percentage of all open source Git repositories are hosted on GitHub including Mint and Cinnamon. So while it's not a direct part of the Git open source project, there's a good chance that you'll want or need to interact with GitHub at some point which is why I chose it. You do not need an account initially to clone my repository from Github.

What this means, without going into details yet is that you can 'clone' (obtain a local copy of) my Git repository which will give you a Working Directory (a folder usually within your home folder) containing all my current applet folders as well as a hidden folder within it called .git which contains all the information to go back through the development history and see every change made between 'commits' which can be thought of as a sort of snapshot. You can also go back ('checkout' in GitSpeak) to any of the 'commits' and every file in the working area will change to what it was at that time. The important points are when a version was uploaded so if my latest version does not work on your machine you can go back and collect the old version. All these important points have had a 'tag' added to them by me to make them easy to find. As time goes on you can 'pull' or 'fetch' my latest version down without having to clone again.

Git is however not the most user friendly piece of software and is mostly used from the command line. However what I have described above is not difficult and there is a first class graphical visualisation program called gitk which makes it easy to see what the commands have done and in the limit you can just clone another time!

That is only a tiny part of what Git can do. You can edit the files for your own purposes and can 'merge' my latest updates in automatically provided there are no conflicts. Even if there are there are ways to handle the conflicts manually. One of the most powerful features is that you can create 'branches' to make your modifications and test them and then merge them back to the master.

Git is now the most widely used source code management tool, with over 40% of professional software developers using Git as their primary source control system. I have therefore decided that my instructions for installing and setting up Git will allow you to domore than just access the various versions (including those more recent than those I upload) but also provide the tools to effective make your own changes and merge tham with any I am making. In theory you could set up your own repository on GitHub and provide your enhancements or bug fixes to me to 'pull' into my version or send me patch files.! Before you worry the whole installation and setup of Git is done in about 9 terminal commands you can cut and pasted from below.

sudo apt-get install git gitk leafpad meld

git config --global user.name "your_username_on_github"
git config --global user.email "yourname@your_git_email"

git config --global core.editor leafpad
git config --global diff.tool meld
git config --global merge.tool meld
git config --global color.ui auto
git config --global push.default simple

git config --global credential.helper 'cache --timeout=7200'

git clone https://github.com/pdcurtis/cinnamon-applets
cd cinnamon-applets

The apt-get install command not only installs Git but its visualiser (gitk), a merging program (meld) and a simple editor (leafpad) which will only be used by Git.

The first two pieces of configuration are to provide a suitable name and email which is added to every commit you make. If you intend to use GitHub there are advantages to having then the same and using your GitHub username rather than your full name.

The use of a simple text editor called leafpad avoids all sorts of problems in using the same editor for your editing of files and within Git. meld is a really good difference tool and is also set to be the default for hand crafting conflicts in a merge. The color.ui auto allows suitable terminal programs to display some information in colour. The push.default simple is to make sure that you only push your master to the remote repository by default - it should not be needed with the latest versions of Git but I started before that was the default so some of my early repositories seemed to ahve a different setting. I have put details of how to obtain the most recent version of Git in an Appendix as Mint 17.x have an earlier versions.

The --global credential.helper 'cache --timeout=7200' means that Git will save your password in memory for some time - here I have set it to 2 hours.

Everything in the configuration with --global is available throughout your machine but needs to be redone on other machines. the command git config --list shows your current configuration. Run it outside of a working directory to see just the global settings you have made.

pcurtis@defiant:~$ git config --list
user.name=pdcurtis
user.email=gitmail@xxxxx.com
merge.tool=meld
core.editor=leafpad
color.ui=auto
diff.tool=meld
push.default=simple
credential.helper=cache --timeout=7200
pcurtis@defiant:~$

The git clone sets up a folder cinnamon-applets (the Working Directory)which is below the folder it is called from, normally your home folder. It populates the Working Directory and including the Git repository (.git) with all the history. It also sets up the links to the remote required to push and pull from it.

Well that was not difficult and you can now see all my applets in ~/cinnamon-applets but do not try editing them yet as there is another stage, namely creating a branch for your tests, to avoid changing the original.

gitk - a graphic viewer for Git.

Now have a look at what you have with gitk which is a graphic viewer for Git. gitk is run in a terminal from within the folder containing the Git repository and helps enormously in helping one see what one is doing.

cd ~/cinnamon-applets
git gitk &

These commands change to the cinnamon-applets folder in home from wherever you were then calls gitk. The & at the end makes gitk run a separate process so the terminal is available to continue with git commands.

The following is a typical output from gitk when I was doing some work on Cinnamon. I have deliberately chosen to show something a little more complex than you will see with my applets to show its power - but you should not worry about the details! I plan to show an example ofthe simplest possible output latter. The following example was actually after a second attempt at merging in a pull request from somebody elses repository having created a new branch for my testing (development) and a extra branch for a particular test when I was investigating a menu problem with Cinnamon (menu-issue).

Firstly you will note that gitk provides a lot of information:

At the top left panel is a list of all the commits with their commit messages and the two panels to the right identify the author, his email and the date. The commits are linked to their parents and children and the SHA1 hash IDs are shown. The position of master, the remote, the branches and tags are shown with the active branch in bold.

Below is full information on the highlighted commit at the top and the changes in every file (in diff format) is normally shown below although it can easily be switched to show a tree on the right with view to the left where every file can be examined.

For completeness I should note there are a number of obscure options which can be specified when you start gitk one of which is worth noting and that is --all which shows all of the branches as wll as the active one:

gitk --all &

I am not going into gitk more here as there are two excellent web pages of explanation on the internet

Back to exploring git after the diversion to look at gitk

Find a list of tags. Now lets go back to NUMA version 2.4.2 which was the one used for 18 months with Cinnamon versions up to 2.8. First we look and see exactly what the tag is before by git tag before the next step where we call checkout:

pcurtis@defiant:~/cinnamon-applets$ git tag
BAMS-1.1.5
BAMS-1.1.8
BAMS-1.1.9
Bumblebee-0.9.5
Bumblebee-0.9.7
Bumblebee-0.9.8
Bumblebee-3.0.0
Bumblebee-3.0.4
NUMA-2.3.17
NUMA-2.3.7
NUMA-2.4.2
NUMA-2.6.0
NUMA-3.0.0
NUMA-3.0.3
NUMA-3.0.5
NUMA-3.0.6
Stopwatch-1.2.2
Stopwatch-1.2.3
Stopwatch-3.0.0
nvidiaprime-3.0.2
nvidiaprime-3.1.4
pcurtis@defiant:~/cinnamon-applets$

Load an earlier snapshot into the working folder. We can now checkout NUMA-2.4.2

pcurtis@defiant:~/cinnamon-applets$ git checkout NUMA-2.4.2
Note: checking out 'NUMA-2.4.2'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b <new-branch-name>

HEAD is now at bc5b1e3... 2.4.2 Changes to automatically select first active interface at start-up Uploaded 6 May 2014
pcurtis@defiant:~/cinnamon-applets$

First note that Git provides a lot of useful information in the terminal.

Now have a look at what you can now see the change with gitk or look at the files in an editor. It is sensible to use gitk to follow even the simple changes here to get a feeling of what is going on and to be able to see the features of each individual commit (remember to refresh gitk from the its file menu). I found gitk very useful, arguably essential for me, to understand Git although it could be considered an overkill at this point

Using checkout to return to the current version. Time to go back to the current version which is master using checkout

pcurtis@defiant:~/cinnamon-applets$ git checkout master
Previous HEAD position was bc5b1e3... 2.4.2 Changes to automatically select first active interface at start-up Uploaded 6 May 2014
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
pcurtis@defiant:~/cinnamon-applets$

Have a look at the files and use gitk and you will find everything is as it was when you cloned it - easy wasn't it?

Useful Git Commands

Before we go much further you need to know some details of a few of the most important commands namely help, init, status, add, commit, log, checkout, branch, diff, merge, clone, pull, push, fetch and stash. Those commands and the explanation which follows is very much a subset or what is available but there are some good 'cheat sheets', including one from GitHub, which you may want to print and stick on the wall

You should use the git help command to get extra information before trying anything complex the first time or use in conjuctiion with the The Pro Git book, written by Scott Chacon also as a pdf.

I am also putting together a list of every command I have every used as an 'aid memoir' and may add it to the repository.

The following covers the commands I use most often and will get you started, you will probably never need all of these.

How to get an overall status report . git status shows you the most important information such as the branch you are on, what files are untracked, changed and staged ready to commit as well as any problems such as incomplete merges. You will use it frequently.

git status

pcurtis@defiant:~/cinnamon-applets$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
              modified: README.md
              modified: status.txt
no changes added to commit (use "git add" and/or "git commit -a")
pcurtis@defiant:~/cinnamon-applets$

You will see that git is normally quite verbose and gives usefull and help information on what you can doo

How to get help for any command: You can get instant information about a command and any options it takes by git help <command>. You can get some useful information by calling help for help itself and try git help everyday which gives accesss to one of several guides and almost does away with writing here! Try them out:

git help <command>
git help help
git help everyday

How to commit a change. A commit takes a snapshot of all the changes you have made in the working directory and saves it complete with the information on the commiter (email and name you set up earlier) and a message. It is then identified by the SHA1 associated with it. I am deliberately simplifying by leave out any reference to staging as you will not need it yet if ever as the following format does everything in one go.

git commit -a -m "Commit Message"

The -a effectively adds every changed file to the commit and the -m allows the message to be given in the same command.

Get a list of commits you have made. The git log enables you to look at a log of what you have done which is most useful to see the commits you have made so you can find their IDs. It shows every commit unless you escape by q but option -n 5 enables you to limit the number to say 5. A useful format to get at the SHA1 hash in short fromat is obtained by option --oneline Try them out.

git log
git log -n 10
git log --oneline -n 8

Go back to an earlier snapshot by checkout <ID> where ID can be the start of the SHA1 hash, the start of the message or a tag or relative to the HEAD. eg

git checkout HEAD~3

takes you back three commits.

Start a new branch: Assuming you have already used checkout to go to a place in the history and if you look at the terminal output you will see that it tells you how to start a branch from that place and switch to it by to make changes. The most common commands are:

git branch <new-branch-name>

Create a new branch from your current HEAD position

Switch to any branch by:

git checkout <branch-name>

And switch back to the master branch by

git checkout master

Merging back work in a branch to your master when you have finished. Following called from master.

git merge <branch>

You may have conflicts when you merge if the master has updates to the same areas of files which you have pulled from the remote repository and the following are two options of what to do.

git merge --abort
git mergetool -t meld

Delete a branch - you are only permitted to do this when the branch has been merged into another branch to avoid loss of information although you can force a delete with loss of what has been done in the branch by the -D option.

git -d <branch>

Marking a point such as a version by adding a simple tag: You can mark a point with a simple tag (label) using git tag <tagname> at the current position or add an ID for a different point by git tag <tagname> <ID>. You can get a list of your tags by just git tag without any parameters:

git tag tag_name
git tag tag_name HEAD~5
git tag

Keeping up-to-date with your remote repository:

git pull

This assumes there are no conflicting changes in work you have done and what has been done on the remote master this will download all the new commits. For information this is from master/origin ie the master on remote origin which was all set up when you cloned so you only need to know about pull at the present!

What are the differences between two commits? This can be as simple as what was in the last commit but sometime you want to know everything that differences between versions or in your current branch. The full command is git diff <ID1> <ID2> The first example is just the last commit, the second example is the difference between the last but two and the last but one commits, both with the output in the terminal (stdout). The third calls your specified gui difference program (which we have already configured to meld) which gives a more useful look at what we have. The last example sends the diff output to a file which can be used as a patch so it has a --no-color parameter as some patch application programs do not like a colored input!

git diff HEAD~1
git diff HEAD~2 HEAD~1
git difftool HEAD~2 HEAD~1
git diff --no-color HEAD~3 > last_three_commits.patch

Applying a patch you have been sent. This is not really basic stuff but I did mention patches above and git has a simple internal command to apply a patch created by git diff . If you start a new branch (for safety) where the patch starts and switch to it you can use git apply < patchfile.patch to apply it to the branch (assumes file in Working Directory), assess it and merge it if you like it.

git apply < patchfile.patch

For information there is another command which creates individual patches for each commit in an email friendly format called git format-patch and if you receive one then you can use git am < patchfile.patch and it will recreate the original patch complete with the commit message and the other commit information.

Push back to a remote repository - you can not push back to my repository but if you have set up your own repository or have forked an existing one and cloned from it all the links will be set up for a basic push from your master to the remote master

git push

The small set of commands above combined with knowledge of how to merge conflicts is all you really need to get most of the value out of Git, in most cases I have given a few extra options to wet your appetite but you can get away with very little.

Exploring setting up branches and merging conflicts:

I have refered several times above to conflicts and sooner or latter one has to face up to dealing with one. The following shows setting up two branches, editing a file called git_applet_intro.txt in both until they are in conflict and merging them using meld and then cleaning up by deleting the merged branches.

pcurtis@matrix:~$ cd cinnamon-applets
pcurtis@matrix:~/cinnamon-applets$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
(use "git push" to publish your local commits)

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: git_applet_intro.txt

pcurtis@matrix:~/cinnamon-applets$ gitk &
[1] 6049

pcurtis@matrix:~/cinnamon-applets$ git commit -a -m "Add help file"
[master 85638c3] Add help file
1 file changed, 74 insertions(+)
create mode 100644 git_applet_intro.txt
pcurtis@matrix:~/cinnamon-applets$ git branch test1
pcurtis@matrix:~/cinnamon-applets$ git branch test2
pcurtis@matrix:~/cinnamon-applets$ git checkout test1
Switched to branch 'test1'
pcurtis@matrix:~/cinnamon-applets$ git checkout test2
M git_applet_intro.txt
Switched to branch 'test2'
########### Made Edits to git_applet_intro.txt
pcurtis@matrix:~/cinnamon-applets$ git commit -a -m "Changes to test2 branch"
[test2 771aa7b] Changes to test2 branch
1 file changed, 4 insertions(+), 1 deletion(-)
pcurtis@matrix:~/cinnamon-applets$ git checkout test1
Switched to branch 'test1'
############## Made Conflicting Edits to git_applet_intro.txt
pcurtis@matrix:~/cinnamon-applets$ git commit -a -m "Changes to test1 branch"
[test1 20e307c] Changes to test1 branch
1 file changed, 1 insertion(+), 1 deletion(-)
pcurtis@matrix:~/cinnamon-applets$ git checkout test2
Switched to branch 'test2'
pcurtis@matrix:~/cinnamon-applets$ git merge test1
Auto-merging git_applet_intro.txt
CONFLICT (content): Merge conflict in git_applet_intro.txt
Automatic merge failed; fix conflicts and then commit the result.
pcurtis@matrix:~/cinnamon-applets$ git status
On branch test2
You have unmerged paths.
(fix conflicts and run "git commit")

Unmerged paths:
(use "git add <file>..." to mark resolution)

both modified: git_applet_intro.txt

no changes added to commit (use "git add" and/or "git commit -a")
pcurtis@matrix:~/cinnamon-applets$ git mergetool

Merging:
git_applet_intro.txt

Normal merge conflict for 'git_applet_intro.txt':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (meld):
########## Resolved conflicts in meld which was opened at theis points. Changes saved before closing it. We now need the commit to complete
pcurtis@matrix:~/cinnamon-applets$ git commit -a
[test2 a6960b1] Merge branch 'test1' into test2
pcurtis@matrix:~/cinnamon-applets$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 4 commits.
(use "git push" to publish your local commits)
pcurtis@matrix:~/cinnamon-applets$ git merge test2
Updating 85638c3..a6960b1
Fast-forward
git_applet_intro.txt | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
pcurtis@matrix:~/cinnamon-applets$ git branch --merged
* master
test1
test2
pcurtis@matrix:~/cinnamon-applets$ git branch --no-merged
pcurtis@matrix:~/cinnamon-applets$ git branch -d test1
Deleted branch test1 (was 20e307c).
pcurtis@matrix:~/cinnamon-applets$ git status
On branch master
Your branch is ahead of 'origin/master' by 7 commits.
(use "git push" to publish your local commits)

Untracked files:
(use "git add <file>..." to include in what will be committed)

git_applet_intro.txt.orig

nothing added to commit but untracked files present (use "git add" to track)
########## Deleted git_applet_intro.txt.orig

pcurtis@matrix:~/cinnamon-applets$ git status
On branch master
Your branch is ahead of 'origin/master' by 7 commits.
(use "git push" to publish your local commits)

nothing to commit, working directory clean
pcurtis@matrix:~/cinnamon-applets$ git branch -d test2
Deleted branch test2 (was a6960b1).
pcurtis@matrix:~/cinnamon-applets$
pcurtis@matrix:~/cinnamon-applets$ git status
On branch master
Your branch is ahead of 'origin/master' by 7 commits.
(use "git push" to publish your local commits)

nothing to commit, working directory clean
pcurtis@matrix:~/cinnamon-applets$

Merging conflicting files: Looking at the above it is obvious that another very important feature which Git has to have is the ability to merge changes from different places such as the remote files on the server or your own branches. This can be done from the command line and it automatic identifies the common root and does a three way merge into the merged file provided there are no conflicts (ie the same lines changed in both files). If there are conflicts they are marked in the resulting file ready for you to edit. But you can also use a GUI tool such as meld as I did above so read on.

Using meld for resolving conflicts during merging - highly recommended: Git also offers the ability to specify and use a graphic merge tool, one of the best being meld. Once the conflict has been identified you just run meld as below (for fictitious conflict in README). I have handled all my conflicts with meld and never done a manual edit.

$ git mergetool -t meld
Merging the files: README

Normal merge conflict for 'README':
{local}: modified
{remote}: modified

Hit return to start merge resolution tool (meld):

When meld opens meld, three areas are shown, and left to right are:

  1. The local branch (usually the master branch)
  2. The current version that you are merging into
  3. The other (or “remote”) version (from the a test branch in example above)

To choose the text from the master branch, click on the black arrow in the left-most window at the top right or from the other branch using the black arrow in the right pane

So far we have covered how you can install and set up Git, clone my repository, examine it with the gitk graphic visualiser, create a branch to make your own modifications and commit them, keep it up to date with my repository with pull and merge in any conflicts btween what I have done and your modification.

Sharing your changes with others.

Let us now look at what to do if you want to share some of your enhancements with others. There are several ways to collaborate but one of the oldest and still one of the easiest if not best is via patches. Patches are small files which contain all the differences which can be applied to another unchanged file or one with, at a minimum, no conflicting changes. This is a basic mechanism used whether you are using Git or not but Git has a few enhancements and makes it all very easy. The Linux Kernel development is almost entirely managed through patches.

First there is git diff which is very like an ordinary linux diff and takes SHA1 IDs or tags or positions relative to the HEAD for the two parameters. There is one caution and that is that Gitis often set up to provide a coloured output to a terminal and it is best to use the --no-color option. If one parameter is missing it assumes the diff id relative to HEAD and both gives the diff from the staging area. HEAD~2 denotes two commits back from the current HEAD

The following makes a patch file for the combination of the last two commits and saves in LastTwoCommits.patch in current folder

git diff --no-color HEAD~2 > LastTwoCommits.patch

The patch can be applied easily by

git apply < /path/to/file LastTwoCommits.patch

There is still a lot of planning needed for successful collaboration to work, for example you have enhanced one of my applets or want to pass the results to me to incorporate. You should, at a minimum:

More Complex Patches: If the patch is complex and has multiple commits a better way to create it tmay be use git format-patch to generate mbox-formatted files. This was originally intended as a way to email to a list by turning each commit into an email message with the first line of the commit message as the subject and the rest of the message plus the patch that the commit introduces as the body. The nice thing about this is that applying a patch from an email generated with git format-patch preserves all the commit information properly. It generates a set of patch files, one for each commit which look rather like the information available in the lower panel of gitk. The name is created from the start of the commit message and when called it outputs the names of the patch files it creates.

pcurtis@defiant:~/cinnamon-applets$ git format-patch nvidiaprime-3.1.4
0001-nVP-Added-Optional-Additions-to-Context-Menu.patch
0002-nVP-NUMA-BAMS-BB-Corrected-icons.png-in-Applet-folde.patch
0003-BAMS-Updated-Changelog.patch
0004-Bumblebee-gputempscript.sh-corrected-back-to-origina.patch
0005-Ignore-.patch-files.patch
pcurtis@defiant:~/cinnamon-applets$

These days it is easier to send the files as attachments as many of email packags do not preserve formating etc information correctly. I have listed the last very simple change here so you can see how full the information is:

pcurtis@defiant:~/cinnamon-applets$ cat 0005-Ignore-.patch-files.patch
From 38e74fa5c9e019715b55a859fd48046c50b2305e Mon Sep 17 00:00:00 2001
From: pdcurtis <gitmail@pcurtis.com>
Date: Tue, 9 Aug 2016 04:11:38 +0100
Subject: [PATCH 5/5] Ignore .patch files

---
.gitignore | 1 +
1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index b1e5d66..9015710 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
*~
netusagemonitor@pdcurtis/vnstatImage.png
*.zip
+*.patch


--
2.7.4

pcurtis@defiant:~/cinnamon-applets$

This patch file can be applied by:

git am 0005-Ignore-.patch-files.patch

This should automatically created the new commit. The author information is taken from the email's From and Date headers, and the message of the commit is taken from the Subject and Body (before the patch) of the email.

Again when applying a patch it should be to a branch starting at the origin of the incoming patch series. If need the the current master can be merged in for testing.

NOTE: I have not tried out git format-patch and git am to check the above out whilst I have used the usual git diff and git apply

Creating your own Git Repository with possible use of GitHub as a central Repository.

We have contrated so far on using, modifying and contributing to an existing repository. However you may wish to take your own applet or other project and create a Git repository to bring it under version control and eventually look at making it available to others as I have done. I will start from scratch but you may have done most of the preliminary setting up already if you have cloned an existing repository.

First we need to install and configure Git. If you thing you have done it before then git config --list will show you what you have (best done outside of a Git repository (folder) so you only see the global settings.

pcurtis@defiant:~$ git config --list
user.name=pdcurtis
user.email=xxxxx@pcurtis.com
merge.tool=meld
core.editor=leafpad
color.ui=auto
diff.tool=meld
push.default=simple
pcurtis@defiant:~$

If you have nothing or are only partially set up then do the appropriate parts of:

sudo apt-get install git gitk leafpad meld

git config --global user.name "your_username_on_github_is_best"
git config --global user.email "yourname@your_git_email_is_best"

git config --global core.editor leafpad
git config --global diff.tool meld
git config --global merge.tool meld
git config --global color.ui auto
git config --global push.default simple

You have at this point two choices depending on what you eventually expect to do.

  1. The second is to add Git to an existing folder turning it into a repository by git init, setting up .gitignore and then making the first commit. It is easiest if the folder is in home. You can of course link to GitHub at a latter stage.
  2. If you are serious about making your work available you may want to set up a GitHub account from the start, create your repository within GitHub, clone it as an empty repository, set up .gitignore, add all your current files and make the first commit which you then push back to the repository on GitHub. This is what I did as I already had a GitHub account from working on Cinnamon and Git was already set up for me. I believe this is worth doing from the start as all the push pull links are made for you if you every decide to use a remote repository.

Avoiding commiting transient files. In both cases you need to go through the Git setup as above and you also need a specific .gitignore file. This file contains a list of files/filetypes etc that git should ignore such as temporary files, patch files, zip files - anything you do not want in every commit as they are transient. Mine is very simple for the applet development but if you are working on code you will want to ignore all sorts of log files, intermediate files and compilation results:

*~
*.zip
*.patch

Linking an existing Git repository: If you have an existing Git repository you can link it to GitHub at a latter stage. First get a GitHub account and create a new repository with the same name as the Git repository (folder) on your machine which should be in home. You then create a remote called origin (by convention) and push your existing work to it. I would always back up the folder first!

cd new-repo
git remote add origin https://github.com/pdcurtis/new-repo
git push -u origin master

The git push -u origin master is only needed the first time and makes sure that you have all the tracking set up - this is the way that is recommended on GitHub

The following goes one further and shows my trial after setting up a new repository on GitHub called git-documents. The are a couple of calls to git status so you can see the effects better.

pcurtis@defiant:~$ cd git-documents
pcurtis@defiant:~/git-documents$ ls -A
.gitignore README.md status.txt
pcurtis@defiant:~/git-documents$ git init
Initialised empty Git repository in /home/pcurtis/git-documents/.git/
pcurtis@defiant:~/git-documents$ git status
On branch master

Initial commit

Untracked files:
(use "git add <file>..." to include in what will be committed)

.gitignore
README.md
status.txt

nothing added to commit but untracked files present (use "git add" to track)
pcurtis@defiant:~/git-documents$ git add .
pcurtis@defiant:~/git-documents$ git status
On branch master

Initial commit

Changes to be committed:
(use "git rm --cached <file>..." to unstage)

new file: .gitignore
new file: README.md
new file: status.txt

pcurtis@defiant:~/git-documents$ git commit -a -m "first commit"
[master (root-commit) c7a06db] first commit
3 files changed, 36 insertions(+)
create mode 100644 .gitignore
create mode 100644 README.md
create mode 100644 status.txt
pcurtis@defiant:~/git-documents$ gitk &
[1] 14048
pcurtis@defiant:~/git-documents$ git remote add origin https://github.com/pdcurtis/git-documents.git
pcurtis@defiant:~/git-documents$ git push -u origin master
Username for 'https://github.com': pdcurtis
Password for 'https://pdcurtis@github.com':
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 1.36 KiB | 0 bytes/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To https://github.com/pdcurtis/git-documents.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
  # check that eveything is set up
pcurtis@defiant:~/git-documents$ git remote show origin
* remote origin
Fetch URL: https://github.com/pdcurtis/git-documents.git
Push URL: https://github.com/pdcurtis/git-documents.git
HEAD branch: master
Remote branch:
master tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (fast-forwardable)
pcurtis@defiant:~/git-documents$

Testing Applets - linking

We now have the ability to develop the applets under version control using Git but for use and testing they need to be within ~/.local/share/cinnamon/applets and that has lots in it we do not want under our version control.

It is a pain to keep making copies so I have instead linked the applets I have been working on rather than make copies but this has the disadvantage that an an accidental update from the Cinnamon Spices web site could lose a lot of updates unless Git had been used to commit frequently but it still seems the lesser of the evils. An example is:

ln -s ~/cinnamon-applets/bumblebee@pdcurtis ~/.local/share/cinnamon/applets/bumblebee@pdcurtis

More Advanced Git use

Tidying up a development branch before merging and pushing to the remote repository

Development of a new feature should be done in a separate branch which is not pushed to the remote repository. One should commit frequently during this stage and not merge back into the master until one has finished and certainly not push to the remote. This can lead to a lot of commits some reversing earlier actions and you may confuse people following your work. Git has many ways to tidy up some easier and safer than others! The simplest is when you want to squash the last few commits into a single new one with a new message which can be done by the reset command.

git reset HEAD~3
git commit -a -m "Merged three commits into one"

The git reset HEAD~3 leaves the working directory unchanged and takes you back three commits and then all the changes are commited in one go.

You can also choose to make the changes selectively - you might for example have made similar bug fixes or enhancements in several applets but want to show them as separate commits with different messages to describe them. That is fairly easy but does need an understanding of the staging area which I have avoided up till now! The git commit -a -m "Merge Message" command does two things. Firstly it adds all the changed files to a list which will be used for the commit and then does the commit. These can be separated and the files you want to commit can be individually added to the 'staging area' or 'index' as it is sometimes called and then a commit without the -a just adds those files in the staging area to the commit snapshot.

Files are added to the staging area by:

git add <filename>

so

git add netusagemonitor@pdcurtis/*.*
git commit -m "Updates to NUMA"
git add stopwatch@pdcurtis/*.*
git commit -m "Updates to Stopwatch"
git commit -a -m "Rest of the updates"

Will split out the changes you have made to NUMA and Stopwatch into two commits and then commit everything else. The only thing to watch is that if you modify a file after it is staged you have to do the add again as the snapshot of its changes has already been taken at the time of the add.

Deleting a remote branch

After a development is complete one will need to tidy up and make sure that your changes have been merged back into your master and the local branch you have used for the development can usually be deleted. If you are doing a collaborative development using Github your changes are usually incorporated via a Pull request from a branch on your Github repository (which is a fork of the repository you are submitting the changes to.) This means that you may need to not only delete your local branch but also the remote branch used to generate the Pull Request.

Deleting the remote branch is not as trivial as one would expect and the 'definitive' answer on stackoverflow on how to delete a git branch both locally and remotely has been visited nearly 4 million times! This article is so popular as the syntax is even more obscure than the rest of git and has changed with version number. I will assume that everyone reading this has at least git version 1.7.0 . I have already covered deleting a local branch and forcing a delete if changes have not been merged above but will repeat here for completeness.

To delete a local branch use:

git branch -d branch_name

Note: The -d option only deletes the branch if it has already been fully merged. You can also use -D, which deletes the branch "irrespective of its merged status."

As of Git v1.7.0, you can delete a remote branch using

git push origin --delete <branch_name>
This is all you need to do if you are just using a single machine for development but if you are using several you may need an additional stage on the other machines in order to locally remove stale branches that no longer exist in the remote. Run this on all other machines:

git fetch --all --prune

to propagate changes.

Getting out of deep trouble - forcing changes to get back in sync between master and origin/master.

These are not actions one would normally use as they change history but, that said, it may be prefereable to pushing all sorts of commits you do not want to the cinnamon-spices-applets repository which have resulted from keeping your repositories in step with the cinnamon-spices-applets repository. In that case your having a fork just as a mechanism to push changes to update your applet one can be a little more flexible in the ways one hides ones mistakes! One is using an abnormal cycle of making ones changes in a branch, pushing the branch when ready to origin (the clone of cinnamon-spices-applets) creating a pull request, when that is complete one one fetches and merges or rebases from the upstream (cinnamon-spices-applets) repository which brings the changes into your master without your having to merge the branch which contained them. Once one is sure the cycle is complete one then deletes the local and remote branch.

Forcing origin/master to be the same as master:

I had to do this after testing a different way to update my local repository from upstream using rebase which removed a lot of merge commits but I then had to either pull and merge from origiin which is my 'clone' of upstream which would have put them all back before I could proceed.

I found this out by comparing origin and master by:

git log HEAD..origin/master

The alternative was to force origin back to be the same as my master. This must not be done if anybody else is using ones repository (ie has cloned it) or there are PLs active as one is rewriting history.

git push -f origin master:master

-f is the force flag. Normally, some checks are being applied before it's allowed to push to a branch. The -f flag turns off all checks. origin is the name of the remote where to push. master:master means: push my local branch master to the remote branch master. The general form is localbranch:remotebranch. This is another way to delete a branch on the remote: in that case, you push an empty local branch to the remote, thus deleting it:

git push origin :remote_branch_to_be_deleted

If you are doing a major tidy up like this it is also useful to find out if you have any branches you do not need left on your remotes
and this can be done by:

git branch -r

after which you can delete them.

Force master to be the same as origin/master

If you want to throw away all your changes on master and want to have it exactly the same as origin/master:

git checkout master
git reset --hard origin/master

Note: Untested.

Collaboration

I have already gone further than I intended but I plan to add some basic ways to collaborate and contribute using Git and remote repositories. The following lighthearted breakdown shows the main methods:

What if you want to send me an enhancement

If anyone wants to contribute any enhancements, bug corrections etc the emailed patch seems the easiest to me although I have just received a Pull request through GitHub and that was very easy for me. No promises I will use Patches or Pull requests but I will certainly try out anything sensible. I do not believe in 'Not Invented Here' but I must understand it if I am going to use it and maintain it, so keep it simple.

Appendix 1

Getting out of trouble using Git

I unfortunately managed to have an Applet which broke Cinnamon preventing any display on my laptop whilst I was away from home. I however found I could do a remote login via SSH to my user giving me terminal access from my phone as the Wifi connection was working. Once logged in I could run Git and commit the files in the faulty version so I could see latter what I had done wrong and then checkout the working master branch and login again using Cinnamon. Two commands and I was back in business. For advanced users - have a look at git stash and git stash pop to avoid a commit you really do not need but be able to get the Working Directory back where it was.

The applet problem turned out to be a faulty edit of the settings-schema.json file where a 200000 had lost the initial 2 giving an upper limit of zero. I would have expected that sort of error to be trapped in the Cinnamon Settings software - I will be be careful in future. I have had two lockouts recently after editing a settings-schema.jason file and had to use a remote login over SSH again.

Appendix 2

Updating to the latest version of Git

I am using Git 2.7.4 from the repositories under Mint 18 but I found under Mint 17.3 that only a much earlier version 1.9.1 was available whilst there are major enhancements in the 2.x.x versions. You can obtain the version by:

git version

I therefore added the “Ubuntu Git Maintainers” team Git Stable PPA to obtain a more recent version under Mint 17.x This is very simple to do with the three following terminal commands:

sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git gitk

After this I had Git 2.9.3 available in Mint 17.3

Appendix 3

Using Git and Github with the latest Cinnamon Applet Development Repository

Much of what has been written above was directed at a different model for collaborative development of my applets to whatone now has to use as there is now a single repository containing all the applets which are available through the Spices Web Ste and the two are connected automatically.

I will gradually update this document starting with this new Appendix which will cover a detailed proceedure for the initial setting up and then submitting a change via a pull request from a branch in a fork of the cinnamon-spices-applets repository.

Hints picked up from various comunications (to be integrated in due course)

From JosephMcc: In general your commits should not have all of those merge commits of upstream/master in them. If you want to pull upstream changes into a branch you are working on you should first update your master branch. git checkout master followed by git fetch upstream and git merge upstream/master.

Once you do that you can checkout your working branch branch and run git rebase master. That will cleanly add all of the upstream changes to your working branch without all the merge commmits.

Whenever you want to start working on a new branch you should first fetch and merge upstream master as described above before creating the branch.

From Odysius: Keep your forked repositories synchronized with the original repositories

Perform these steps periodically to ensure that you are working with updated code.

NOTE: The above two hints differ in the use of rebase versus merge to keep up-to-date. I have always used merge not rebase which has sometimes ended up with a series of merge commits which is undesirable if one is just keeping up-to-date because I was not sure I understood rebase and it advantages. I will try the rebase alternatives in due course.

Before You Leave

I would be very pleased if visitors could spare a little time to give us some feedback - it is the only way we know who has visited the site, if it is useful and how we should develop it's content and the techniques used. I would be delighted if you could send comments or just let me know you have visited by sending a quick Message.

Home Pauline Howto Articles Uniquely NZ Small Firms Search
Copyright © Peter and Pauline Curtis
Content revised: 18th March, 2017