How to: Use branches in Git (Part V)

How to: Use branches in Git (Part V)

So, essentially branches are the way to go when trying out new ideas or isolating features or sections of work. One can branch out of the master line and work on a feature, independently from what’s going on the outside, little introvert f*ckers branches are…

Git handles context switching very fast, meaning that switching branches replaces the files in the working directory with those of the selected branch without too much hassle. Eventually, branches can be either merged into the master branch or discarded completely.

git branch

By itself, the command displays the branches of the current repository:

git branch          # Show the local branches
git branch -v       # Show the remote branches 
git branch -a       # Show all branches, including remotes

To create a branch out of thin air, use this command and give it a nice name (no, Branchie is not a nice name):

git branch <significant-branch-name>

Of course, if you f*ck up, you can always rename it using:

git branch -m <old-branch-name> <new-branch-name>

Git-Tree
It is also possible to delete the branch, but note that the more branches you remove, the more your git tree will start looking like a giant wooden buttplug:

git branch -d <delete-me>

Note that if a branch was not fully merged, that command won’t do it, but you can force it to do it anyway and it won’t even mind:

git branch -D <delete-me>

And now it’s probably the time to bring back our old pal, git checkout. Apart from replacing your local files, it can also switch branches, thus moving the pointer to the new currently active branch and replacing the contents of your working directory.

git checkout <branch-name>

It can also do super combos: create a new branch, make it reference another branch or tag and switch to it.

git checkout -b <new-branch> <parent-branch>

Another quick option that can come in real handy, git branch --merged will display branches that were merged and basically contain each other’s commits, more precisely it displays a list of all the branches that contain all the commits that the currently active branch has.

Note however, that Git will not allow branch switching if the current branch’s working directory is not clean, in the sense that there are no files in conflict – there’s no problem with new files or changed files that have not been altered in other branches. In the event of possible conflicts, either stage and commit the changes, or remove them.

git merge

To merge a branch, first checkout the branch you want to merge things into, then run the git merge command.

git checkout master
git merge <branch-name>

That command will add the commits from <branch-name>, on top of those on the master branch and then move the HEAD pointer at the top of the list. But, based on the level of their complexity, merges can be of two types: fast-forward merges and real merges.

Fast-forward merges occur when the parent branch doesn’t contain additional changes from the time of the branching, meaning that it’s absolutely safe to just move the source branch’s commits at the end of the parent branch and then simply update the HEAD pointer with the last position in the chain.

Real merges occur when the parent tree contains additional commits that were submitted in the meanwhile, between the times of branching and merging. Git tries to auto-merge branches using a recursive strategy. But there can always be such things as merge conflicts. 

Conflicts are usually handled automatically if the file differences are on different lines, but otherwise, put yourself in Git’s position. Without any knowledge of what any of those lines mean, how do you decide which version is more adequate or better yet come up with a hybrid solution that takes incorporates both changes?

When that happens, Git throws a conflict error and the merging operation is suspended until the conflicts are resolved. So, the files that are in conflict now contain both blocks of code, enclosed between lines of <<<<<< and ====== delimiters.

Manually reviewing those changes and removing the delimiters is preferable to using automatic merge tools as it is considered safer. After the conflicts are fixed, we can add the files to the staging index and perform a simple commit without any message – that will be prompted by the merging operation itself in a freshly opened editor.

Now that we talked about the two types of merges, here are a couple of useful options:

The first one inhibits fast-forward merges and forces git to copy all the commits from the branch to the parent tree, thus maintaining a detailed history log, even if fast-forwarding would have been possible, in which case only one commit would have been created on the parent branch to indicate the fact that there has been a merging operation:

git merge --no-ff <branch-name>

The other one will perform a merge only if fast-forwarding is possible and abort the operation otherwise:

git merge --ff-only <branch-name>

To reduce the probability of having conflicts occur when merging branches, we can merge the parent branches into the working branches, and do it often – this way when the working branches will pretty much contain the same thing as the parent ones, thus making it easy for git to perform successful auto-merges or even fast-forward merges. This strategy is called tracking and is commonly used when working with remotes.

Note: It is advisable to only perform merges with a clean working directory. Otherwise, things could get messy and we would have to talk about using stashes. Oh, what the hell – let’s get it over with:

git stash

I know it sounds like a porn term, but in actuality, the stash is a special fourth area in Git. It is separate from the repository, the staging area or the working directory and doesn’t have a checksum associated with it.

Stashes are commonly used when trying to switch into a branch while the working directory is not clean. Typically you would have two options: discard your changes or commit them. The third option would be to stash those changes and deal with them at a later time.

git stash save "Message goes here"

What the command really does, is it stores the changes into the stash with the specified message, so that they can be accessed later, and then performs a hard reset to HEAD e.g. git reset --hard HEAD, taking the pointer back to the previous commit.

Here’s another fun command, to display all the stashed entries, with their associated identifier and message.

git stash list

Just for future reference, a stash identifier looks something like this: stash@{<number-of-stash>}

This next one displays a diff between the changes in the stash and those in the HEAD.

git stash show -p <stash-identifier>

And well, here’s the rest of them:

git stash apply <stash-id>  # Apply <stash-id>
git stash pop <stash-id>    # Apply <stash-id> and remove it from stashes
git stash drop <stash-id>   # Remove <stash-id> from stashes
git stash clear             # Remove all stashes

That’s it for today. Marilyn Monroe once said “if you can make a woman laugh, you can make her do anything”, to which I must add that the same thing applies if you can make her branch. Next time we can already start talking about remotes, moving up in the world like that… Here you go: How to: Work with remotes in Git (Part VI).

Advertisements
How to: Ignore files in Git (Part IV)

How to: Ignore files in Git (Part IV)

Ignorance is blissWith highly active projects, tracking irrelevant files can quickly become a pain in all of the asses that are working themselves off in the process.

I’m talking about those pesky little ever-changing files, machine or man generated, like log files, cache files, sql dumps, 3rd party project files for various assets, plain old text files some random co-worker keeps throwing in and so on.

Luckily, Git comes with a very useful feature for that too – it lets you ignore files and you can do it very easily in two slightly different ways:

On a project basis: Create a .gitignore file and put it in the root directory of the project e.g. /var/www/public_html/your-project.

Globally: Create a .gitignore_global file, put it in your home folder and add it to Git’s global config:

git config --global core.excludesfile ~/.gitignore_global

The contents of the file(s) can look something like this:

*.sql         # Ignore all .sql files
*.ini         # Ignore all .ini files
/tmp/*.*      # Ignore all files in /tmp
/cache/*.*    # Ignore all files in /cache
*.log         # Ignore all .log files
!change.log   # Don't ignore change.log

Note: Git will not ignore a file that was already tracked. To delete a file from staging index after it has been ignored, do git rm --cached <file>

Note: Git will not track empty directories, it just tracks files and the directories it takes to get to
them. In order to track empty directories, just put an empty file in them called .gitkeep e.g.
touch <empty_directory>/.gitkeep

You know it, I know it, ignorance is bliss, but keep it mind that too much of anything will get you nowhere (that’s the best case scenario). I think it’s about time we move on to more serious matters, like branching – it’s ok, adults do it all the time. Follow the next post in the series on How to: Use branches in Git (Part V).

How to: Undo changes with Git (Part III)

How to: Undo changes with Git (Part III)

Sometimes, you f*ck up. I f*ck up, you f*ck up, everyone f*cks up at some point, we all do it, it’s natural. F*ck, some people actually even make a living out of it and sadly, it is often the same people we look up to or those who run the country. But f*ck it. This section is still all about Git and Git is the magical land where f*ckups are undoable.

what-has-been-seen-cannot-be-unseen

However, in most situations it is probably more preferable to just log the mistakes too and make new commits that fix them to reflect an accurate change history.

But no matter the weather, even in dire situations where there have been more commits in the meanwhile, on top of the ones we’re trying to undo, we have a few options:

git checkout

Even though git checkout can help restore peace and order in your working directory, we’ll have to talk about it later too when we reach branching, as it’s primary purpose is way greater. Here we can use it to replace files in the working area with the files stored in the repository (at the current HEAD pointer or any ref pointer really). But note that, when not using branches, it is good practice to include a double bare dash -- to avoid confusion and any unforeseen consequences:

To restore a file in the working directory with the file located at the current HEAD in the repository:

git checkout -- <file>

To restore a file with the file located in the repository at a specific commit pointer:

git checkout 24f6daf6f -- <file>

Looks like we’re going to need to talk about the checksums anyway… Git generates a checksum for each change set, using the SHA-1 hash algorithm, which helps maintaining data integrity. It is the checksum that can be used to refer to a specific commit.

Typically, a SHA1 hash value is rendered as a 40 digits long hexadecimal number, but because of how unique they all are, it is considered safe to address them by only their first few characters (8 characters for instance is way safe).

git revert

This command is used to revert an entire commit. Well, in actuality it creates a new commit which mirrors the targeted one (i.e. everything that was added gets deleted, everything that was deleted gets added, thus all changes undone).

git revert 24f6daf6f

So, using the above command creates the new commit and opens your editor so you can customize the commit message.

git reset

Most commonly, you could find yourself using the reset command to do one of the following two things:

  • Unstage files: say you’re trying to build up a nice commit, stating multiple files and then by accident, a file that’s not supposed to be there just slips in and your staging area is ruined…
git reset HEAD

There we go, that command had the sole purpose of unstaging that file.

  • Undo multiple commits: using the following commands will actually move the HEAD pointer to a previous commit. We have three useful options for three different types of resets, as follows:

The soft reset is the safest reset there is, as it doesn’t change the staging area or the working directory:

git reset --soft 24f6daf6f

The mixed reset is the default option, it changes the staging area but not the working one:

git reset --mixed 24f6daf6f

The hard reset can be destructive as it changes files in all three areas – USE WITH CAUTION.

git reset --hard 24f6daf6f

Thing is, once the HEAD pointer moves back in time, all the other commits after it might seem lost, but they’re not – you can run the same command again, referencing any future commit you can think of (you can first output a git log to a plain text file to still have access to those checksums).


By now I assume you’re old enough to know that not every project is made of candy and each one of them is totally worth tracking and just as important as the next one. There will always be such things as small OS-specific temp files, cache files, logs, dumps and stuff that constantly changes just enough to make git flip out on every commit you make. In the next post, we’ll have a serious grown-up talk on How to: Ignore files in Git (Part IV).

 

Getting started with Git (Part II)

Getting started with Git (Part II)

After just a little over a week’s time from the first part of this series – Getting started with Git (Part I), I’ve managed to put together a basic, yet hopefully comprehensive list of git commands that should get you right on track with your workflow. Before anything, you must understand that initially, this post was over 30 printable pages long, which made me seriously doubt my mental capacity and then split it into multiple posts, one for each major section of it.

THE BASICS

Now, just to make sure we’re on the same page, know that Git doesn’t quite exactly track your files, but rather the changes you make to them. While this might seem useless in the event of a filesystem catastrophe (i.e. your files get lost / deleted / abducted by aliens and / or Pedobear), you best believe that it is more than enough to restore everything back to its previous state, provided that your project directory is still a git repository – which you can easily tell by the presence of a hidden .git folder in the root of your project.

Furthermore, with Git, you can actually restore the project to any state at any point in its development history, for which there is a commit entry. And even in case your whole directory is gone, you can still hope someone else has a copy of your repository and / or you can just clone the entire project from a remote… (you did use one, right?) Great, let’s get to it – this section will get you started with some basic commands for managing your git repository and making changes to it.

git help

The first command you might want to take a look at before anything if you’re just starting out, is git help. On its own, without any arguments, it will give you some general usage tips and a list of the most commonly used git commands, which is far less intimidating than the initial length of this post.

But the true power of this command lies in its ability to be used with other git commands to display their specific help page. Go ahead, give it a try for the next command we’re going to talk about:

git help clone

git clone

Sharp little young grasshoppa’ you are, aren’t you? You already know that git clone does wonders for your sheep then… Well, another thing it does is clone a repository into a newly created directory, creates remote-tracking branches for each branch in the cloned repository and checks out an initial branch that is forked from the cloned repository’s currently active branch.

Sheep Cloning

You can actually clone from local repositories too, but this command is more commonly used when working with remotes. Just as in the case with most of the commands described in these posts, this one has a bunch of interesting options that are thoroughly described in the man pages, which you should check out whenever in doubt, or ask questions in the comments section below. Up to some level, I’m somewhat of a guru and might even find myself posting questions and answering them later – which might be sad and funny at the same time, but all in all normal.

There are multiple variations on Git URLs and they can all be used with this command. Some of the most common forms would be:

  • ssh://[user@]host.xz[:port]/path/to/repo.git
  • git://host.xz[:port]/path/to/repo.git
  • http[s]://host.xz[:port]/path/to/repo.git

I’ve already posted a quick guide on how to make a git server that gives you those ssh:// URLs, and there’s another one on the way for a more elegant and secure solution that gives you the git:// URLs which are simply just prettier.

Then, a regular clone command would look something like:

git clone git://git.server.net/git/repo.git

That command will create a repo directory in your CWD, but you can override its default name by adding it as an extra argument:

git clone git://git.server.net/git/repo.git CustomName

git status

So basically, git status displays paths that have differences between the… wait. Did I tell you about the 3 Tree Architecture? Yeah, well Git uses it, as opposed to most other version control systems that are stuck at 2 Tree. Simply put, that only means Git consists of 3 areas or places to store things. Oh, f*ck it – too much talk – here’s a picture:

Git's 3 Tree Architecture

So, as I was saying, git status displays paths that:

  • have differences between the Staging Area and the Repository (can be committed)
  • have differences between the Working Copy and the Staging Area (can be added, then committed)
  • are simply not being tracked by Git and can be added (and are not ignored, more on that later…)

This is already too long of a post, so I might as well talk a little bit about some of these terms:

Working copy would be your actual working directory on your local machine where you make your changes.

Staging Area is that magical place where pending changes (files that were added using git add) wait to be committed – think of it as an intermediate step that allows you to build custom commits, organize your changes into commit batches.

Repository is the final destination of your changes, the place that stores your commits and their specific messages.


Committing is the process of moving the changes from the Staging Area to the Repository.

Adding would be the process of telling Git to start tracking a file or group of files and adding it to the Staging Area.

At this point, I sure as hell hope that all this typing will somehow offload the task of having to explain every minor little thing that will come into context… It’s all nice and interesting but not of utmost importance for someone who’s only going to be typing these commands and move on before it all becomes routine.

On a second bipolar thought, I’m glad I know about these things and if I ever stop knowing, at least I could get a good idea on what to want to know. My logic is impervious… and this, having been a long day is no good excuse. This post will either amuse me later on when I’ll review it or cause some serious self-pity as I go all emo.

git commit

At long last… wait. Did I tell you about the SHA checksums? Kidding… for now. But really, if you’re afraid of commitment, this is where you should stop reading ’cause we about to commit. Git it? We’ve already covered what the commit does, moving stuff from the Staging Area to the Repo, you only need to know that any commit needs to have a message associated with it (I keep hearing there will be an awesome post on writing good commit messages soon):

git commit -m "Your significant and concise commit message"

Note: You cannot commit files that Git is not currently tracking.

To start tracking files, you need to git add them:

git add /path/to/file.ext

You can add multiple files simultaneously and even use wildcards, or you can just simply add all the files in your project:

git add .

Another useful thing to know would be the shorthand for adding all files and committing at the same time:

git commit -am "Yet another fascinating commit message"

Yeah, and then there’s the --amend option – you can use it to squeeze in another commit within the last commit in case of a minor f*ckup (i.e. you forgot to add a file or change a title somewhere):

git commit --amend

That command will open the core editor allowing you to change the commit message of the most recent commit. Additionally you can set the commit message directly from the command line:

git commit --amend -m "The new and improved commit message"

Yes, the amend option comes in handy even when there aren’t really any changes and you only want to just maybe correct a typo in the previous commit message or something.

git log

The command that lets you view all of the commits with their messages and other associated data – a command with many many options and a man page that’s really really worth going through. Soon as you start loving it, you’ll realize how important good commit messages are… Here are just a few tips:

git log                   # To view all the commits ever made
git log -n                # To limit the number of commits
git log --oneline         # Compact view, for large projects
git log --graph           # To visualize branch merges
git log --author="John"   # View only commits made by John

Also, you can filter commits by date:

git log --since=2014-09-03       # Commits since 2014-09-03
git log --until="two weeks ago"  # Commits until 2 weeks ago

Oh, it gets better – with regular expression filters:

git log --grep="[bugfix]"        # Look for bug fixes
git log --grep="[css]"		 # Look for CSS

Yes, the first line of commit messages could include tags, so that you can later on use git log to filter them…

git diff

Yeah… I’m not going in depth with this one – it’s not really much to it, but for my diff-ing needs I usually rely on third party apps / plugins since I’m not even that much of a fan of Unix’s diff command either.

The git diff is quite similar to it but works with different types of arguments like git trees or tree-ishes, blob objects, actual files on disk and all kind of combinations of all of the above. The official man page does a way better job describing it all than I ever even would be willing to.

But I don’t wanna be the bad guy here, so here are a couple of tips:

git diff             # Working vs. Staging for all files
git diff <file>      # Working vs. Staging for <file>
git diff --staged    # Staging vs. Repository

The command can also be used to compare two or more branches:

git diff <branch-1>..<branch-n>

Note: The --color-words option can be used to suppress duplicate lines in the output and highlight only the changed parts.

git rm / git mv

There’s nothing really special about these two except the fact that they handle file moving, renaming or deleting inside a git repository the right way.

Their Unix-specific counterpart commands have their ways of affecting Git’s staging area by messing up the tracked and untracked files (that’s a pretty long and boring story) but I don’t mind a few extra steps cleaning after them so, yeah…


Committing stuff regularly is generally a good thing, think of it like the save / autosave feature in well, really any respectable game out there. Besides that, you have full control over when and what to save, and even then, sometimes things can go horribly wrong. In the next post we’ll go over a bunch of ways that we can Undo changes in Git (Part III).

 

Pi-Blaster: Improved Software PWM

Pi-Blaster: Improved Software PWM

What is Pulse-Width Modulation?

Pulse-width modulation (PWM), or pulse-duration modulation (PDM), is a modulation technique that controls the width of the pulse, formally the pulse duration, based on modulator signal information. (More information on Wikipedia)

PWM on the Raspberry Pi

Without an external DAC circuit, getting PWM signals out of multiple pins on the Raspberry Pi can only be achieved by software (thus, the terms software pwm / soft pwm / bitbanging), switching the pins high and low at precisely controlled time intervals.

While having its own limitations, this technique might prove itself satisfactory for projects that do not require perfect signals (i.e. servos). But even then, in a situation where time is of the essence, any piece of software of that kind becomes useless in the face of Pi’s limited CPU resources. As this solution would run simultaneously along with other processes, one cannot entirely rely on the clock cycles needed to switch the state of the pins at exactly the right time.

The Pi-Blaster Project

And this is where Thomas Sarlandie‘s excellent Pi-Blaster comes into play. Just like it’s ancestor, ServoBlaster, it cleverly offloads the task of switching the pins to the direct memory access (DMA) controller. The technique used is extremely efficient: does not use the CPU and gives very stable pulses. I would call it a hybrid PWM solution since it’s basically software that uses some other hardware to get the job done.

Installation

sudo apt-get install autoconf
./autogen.sh
./configure
make

To start pi-blaster and have it relaunched automatically on every reboot:

sudo make install

If you ever need to stop pi-blaster and prevent it from starting automatically on the next reboot:

sudo make uninstall

How to use

Pi-Blaster creates a special FIFO file in /dev/pi-blaster. Any application on your Raspberry Pi can write to it (this means that only pi-blaster needs to be root, your application can run as a normal user).

Important: when using pi-blaster, the GPIO pins you send to it are configured as output.

To set the value of a PIN, you write a command to /dev/pi-blaster in the form <GPIOPinName>=<value> where <value> must be a number between 0 and 1 (included).

For a short example, this is how we set GPIO pin 17 to a PWM of 70%:

echo "17=0.7" > /dev/pi-blaster

More information on the available options, usage examples and other articles is available on the Pi-Blaster project page on github. I’m sure the entire RPi community shares my endless gratitude towards the guys who made it all possible, Thomas Sarlandie and Richard Hirst.

Gource: Visualize your Git logs in style

Gource: Visualize your Git logs in style

While generally there isn’t any real benefit in using this tool, it has been known to be able to increase your swag factor by up to 83% and it is, in the context of version control, the ultimate eye candy, an absolute must-have for catholic priests and Pedobear fanboys.

Gource was developed by Andrew Caudwell as a software version control visualization tool with built-in log generation support for Git, Mercurial, Bazaar and SVN. To find out more about it, you can check out the official page on code.google.com.

Software projects are displayed by Gource as an animated tree with the root directory of the project at its centre. Directories appear as branches with files as leaves. Developers can be seen working on the tree at the times they contributed to the project.

Gource Screenshot

Windows users can download it from the Downloads section of the project page, install it and run it normally from a command prompt, while in Linux, you can just install it from the main repository:

sudo apt-get install gource

To use it, just navigate to the working directory of a git repository and run it:

cd /path/to/your/repo
gource

or, run it directly by just providing the directory as an argument:

gource /path/to/your/repo

Here’s an absolutely remarkable video showing 21 years (1991 – 2012) of the Linux Kernel development process in somewhere around almost three hours:


Check out the help/man pages for a list of all the options and how to use them. For instance, I sometimes use this as a screensaver:

gource -1920x1080 --fullscreen --multi-sampling -a 0.1 --time-scale 3.0 --loop -i 3600

Oh, and here are some fun interactive keyboard commands:

(V)         Toggle camera mode
(C)         Displays Gource logo
(K)         Toggle file extension key.
(M)         Toggle mouse visibility
(N)         Jump forward in time to next log entry.
(S)         Randomize colours.
(+-)        Adjust simulation speed.
(<>)        Adjust time scale.
(TAB)       Cycle through visible users
(F12)       Screenshot
(Alt+Enter) Fullscreen toggle
(ESC)       Quit

The documentation is a lot, but well structured and worth reading + there’s even a guide for recording / encoding videos if you’re not into third party video capture software.

How To: Fix the sound volume issues in VLC Media Player

How To: Fix the sound volume issues in VLC Media Player

Yeah, another quick post on how to work out the dynamic audio compression settings in VLC. By default, you get these awkward moments when you go to sleep, put on a boring movie and set the volume just right so that it’s kind of soft, yet any dialogue is still just a bit more than perceptible and then slowly your reality is mixing with the movie and you’re out. So there’s this party and then these two guys are out, talking, and then a phone rings and all of a sudden 800 Watts of pure RMS explosions and gunfire shake the whole house, waking the dead and they start screaming, you jump out the bed and now you’re f*cking screaming and there goes a classic dafuq moment.

If you don’t know what I’m talking about, this post is not for you. And if you do know what I’m talking about but won’t admit it because you get startled like a schoolgirl whenever a truck just rolls by in mid-scene even when you’re genuinely doing nothing else but watching a movie, read on – your secret’s safe with me (just leave a comment with your name and the name of the non-horror movie that made you wet your pants most recently and I’ll make sure not to mention it randomly in an unrelated upcoming post).

Step 1

So, first of all, if you liked what you saw in my How To: Make Linux Look Sexy In Only 3 Fast Steps post, you already know that VLC’s default skin looks absurd and that Maverick07x’s MinimalX skin on deviantart.com is a must-have. Here’s what it looks like:

MinimalX Skin for VLC

The reasoning behind the previous paragraph’s off-topic-ness is that even without dynamic audio compression to prevent unpleasantries, a good looking skin can help lower blood pressure faster and make you look less like a startled gazelle on her period.

Step 2 

Go to preferences / Advanced Preferences (Ctrl + P), in the lower left corner select All to show all settings and then, in the left sidebar under Audio, click Filters. You want to check the Dynamic range compressor but I also suggest checking Volume normalizer and Parametric Equalizer – they might come in handy at some point. Note that omitting this step causes VLC to reset your settings every time you close it.

VLC Advanced Preferences

Step 3

It’s about time we enable the Compressor and configure it properly. Right click, go to Interface, Effects and Filters (Ctrl + E) and there you have it on the second tab under Audio Effects. The values you’re about to see have been tested, they give good results so no questions asked.

VLC Audio Compressor

If you want to know what they mean and how sh*t works, you need to looks somewhere else. I too did this research once, I don’t take credit for these settings and out of about 5 different (yet so similar, in terms of setting values) sources, only one of them explained them properly. It was an interesting read at that time but didn’t find it interesting enough to remember any of it – this is one of those type of things I just like to get done quick and move on. If you have too much time on your hands and feel like getting them dirty, you can experiment with different values – who knows, maybe you can achieve even greater results.

On a slightly different note, I hate Windows users (myself included every once in a while) for having KMPlayer available for them just like that. I dare say it’s the best media player there is – everything works out the box, has its own internal codecs, it’s highly customizable, has tons of handy little keyboard shortcuts and an impressive f*ckload of options in the settings panel that can intimidate even the most versatile tech savvies. It also looks great just with its default skin, but with LangLe’s KMetro from deviantart.com it’s just porn for the retina.

KMPlayer KMetro Skin

Oh, did I mention that these settings described here barely manage to come close to what KMP just does by default? Oh well, at least it’s available on Android, maybe there’s still hope…