Tuesday, March 23, 2021

Remove git orphan branches for *nix based OS.

I've already posted how to remove orphan branches if you develop on windows. This post is just a small addition but for those who develops on *nix based OSes. 

The problem is very simple, you develop something, you push it, you get your branch merged, then you need manually remove local branches. It can be easily automated, first we need to run

git branch -vv

and then remove all branches that have no remote branches.

git branch -D branch_name

It's possible to write a simple bash script that checks git branches and then just removes ones that no longer have remote branches. I'm not a huge fan os bash so I decided to go with python. It's more convenient, predictable and easier to use. Bellow is the script itself.

#!/usr/bin/env python
import subprocess

cmd_output = subprocess.check_output('git branch -vv', shell=True)

output_lines = cmd_output.split('\n')

to_remove = []

for line in output_lines:
    if line.startswith('*'): # skip current branch
        continue

    if 'gone' in line: # there is no remote branch
        line_segments = line.strip().split(' ')
        branch = line_segments[0]
        to_remove.append(branch)

if not to_remove:
    print('There is nothing to remove, consider running `git fetch` and `git remote prune origin`')

for branch in to_remove:
    subprocess.call('git branch -D ' + branch, shell=True)

Let's save this file as git-clean. Of cause you can use whatever name your want, but then please adjust the instructions bellow. 

In order to make it more convenient to use let's make it executable by running 

chmod +x git-clean

and add this executable to the PATH variable. Simply go to your home folder and find there the .bash_profile. Open it and add the path to the folder where your git-clean is located. In my case it's ~/projects/tools.

export PATH="~/projects/tools:$PATH"

Now restart your terminal and  git-clean should be globally available.

P.S. this script uses python 2.x. and with python 3.x is should be possible to achieve the same result easier.


Tuesday, September 3, 2019

Remove git orphan branches

Quite often especially when you create a pull request for some short time you have a local branch and a corresponding remote one that you track. But after your pull request is merged and remote branch is removed you still have your local branch.

If you run command

git remote prune origin

It will check if branches that you track are still there, if not  then your local branches are orphans now.

If you run now

git branch -vv

Then you will get a nice info about each branch


Here we can see that branch_a is gone, git literally says it. This is our orphan branch. No we can run the following command to remove it.

git branch -D branch_a

But what if we have 5 or 10 branches that became orphans? It would be nice to have some helper function that automates this process.

If your working environment is windows you can easily add a helper function into your powershell profile. Just go to powershell console and run

notepad $profile

And add the code bellow at the end of your powershell profile.

function git-clean() {
 git branch -vv | Where {$_.Contains("gone")} | ForEach -Process { git branch -D $_.Split()[2] }
}

Now every time you open a powershell console it will scan your profile and import all functions declared there. Now you can simply run git-clean and it will do all magic.

As summary just run


git remote prune origin
git-clean

P.S. if you have just added this function you need first to restart you powershell console to make this function available.

Friday, November 3, 2017

Recharts bar chart with multiple colors

I needed once to add a bar chart using Recharts  library but with multiple colors, with two coloros rotated in the bar. Something like this:



There is no such functionality out of the box, but it's doable. Recharts provide a possibility to customize bar shape. Bar component has an attribute shape that accepts a function or a component, to render the shape of the bar.

Since Recharts uses SVG under the hood the first thing I did I found how people get such shapes using pure SVG. To achive that we need to define 2 things: a pattern and a mask based on that pattern.

<svg width="120" height="120" viewBox="0 0 120 120"
    xmlns="http://www.w3.org/2000/svg">

  <!-- Pattern -->
  <pattern id="pattern-stripe" 
        width="8" height="8" 
         patternUnits="userSpaceOnUse"
         patternTransform="rotate(45)">
    <rect width="4" height="8" transform="translate(0,0)" fill="white"></rect>
  </pattern>
  <!-- Mask -->
  <mask id="mask-stripe">
   <rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-stripe)" />
  </mask>
  
  <!-- Reactangle that uses mask -->
  <rect x="10" y="10" width="100" height="100" mask='url(#mask-stripe)' />
</svg>

I put the mask and the pattern in the SVG tag and also a rect tag that uses the mask to define how this react will be filled. If you save it as SVG file and open in a browser you will see something like this:


It's pure SVG and it has nothing to do with react and recharts. However we already know that we are on the right track.
Let's create a function that will render a colored rectangle:

const CandyBar = (props) => {
 const {    
    x: oX,
    y: oY,
    width: oWidth,
    height: oHeight,
    value,
    fill
  } = props;
  
  let x = oX;
  let y = oHeight < 0 ? oY + oHeight : oY;
 let width = oWidth;
  let height = Math.abs(oHeight);

 return (
   <rect fill={fill}
          mask='url(#mask-stripe)'
          x={x}
          y={y}
          width={width}
          height={height} />
    );
};

No we can use it as shape parameter for the Bar component. There is nothing interesting here, we just so cover here the case when height is negative, it happens when we need to display a negative value.  We also set here the mask, to define how the react will be filled. This is how looks a bar with custom shape:

 <Bar dataKey="a" fill="green" shape={<CandyBar />} />

Now let's put everything together:

const data = [
      {name: 'Page A', a: 400, b: 240, c: 240},
      {name: 'Page B', a: 300, b: 139, c: 221},
      {name: 'Page C', a: -200, b: -980, c: 229},
      {name: 'Page D', a: 278, b: 390, c: 200},
      {name: 'Page E', a: 189, b: 480, c: 218},
      {name: 'Page F', a: 239, b: -380, c: 250},
      {name: 'Page G', a: 349, b: 430, c: 210}
];

const CandyBar = (props) => {
  const {    
    x: oX,
    y: oY,
    width: oWidth,
    height: oHeight,
    value,
    fill
  } = props;
  
  let x = oX;
  let y = oHeight < 0 ? oY + oHeight : oY;
  let width = oWidth;
  let height = Math.abs(oHeight);

  return (
   <rect fill={fill}
       mask='url(#mask-stripe)'
          x={x}
          y={y}
          width={width}
          height={height} />
    );
};



const CustomShapeBarChart = React.createClass({
  render () {
    return (
    <div>    
      <BarChart width={700} height={300} data={data}
            margin={{top: 20, right: 30, left: 20, bottom: 5}}>
        
        <pattern id="pattern-stripe" 
         width="8" height="8" 
         patternUnits="userSpaceOnUse"
         patternTransform="rotate(45)">
         <rect width="4" height="8" transform="translate(0,0)" fill="white"></rect>
        </pattern>
        <mask id="mask-stripe">
        <rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-stripe)" />
        </mask>

         <XAxis dataKey="name"/>
         <YAxis/>
         <CartesianGrid strokeDasharray="3 3"/>
         <Bar dataKey="a" fill="green" isAnimationActive={false} shape={<CandyBar />} />
         <Bar dataKey="b" isAnimationActive={false} fill="red" />
         <Bar dataKey="c" isAnimationActive={false} shape={<CandyBar fill="#8884d8" />} />
      </BarChart>
    </div>
    );
  }
})

ReactDOM.render(
  <CustomShapeBarChart />,
  document.getElementById('container')
);

It will render fallowing chart:



Here is a working fiddle

There are several important things here:
1. BarChart component generates SVG tag.
2. Mask and Pattern tags should be inside SVG tag. That is why we put them as children of the BarChart
3. Bar component uses CandyBar to define how the shape will be rendered.
4. CandyBar generates a rect with the proper mask that is located by Id.

Sunday, August 6, 2017

React datetime component, disable keyboard input

We currently use react on our current project and react-datetime library to render datetime picker.



Recently we got a requirment to disable keyboard input, so that users can choose a date only from the calendar UI. Unfortunately this functionality is not availbale out of the box. But there is a way to do it.

ReactDOM package provides a method to locate html element that contains react component.

ReactDOM.findDOMNode(this);

The simplest solution would be to wrap <Datetime /> component with a custom one and then disable user input, simply suppress all keyboard interaction.

Each react component has a set of hooks , one of them is componentDidMount. This method is invoked once when component is mounted. This is the right place to do some magic.

componentDidMount() {
  const componentPlaceholder = ReactDOM.findDOMNode(this);
  $(componentPlaceholder)
   .find('input')
   .on('keydown', () => false);
}

We simply search for the html element that contains the component and then search for an input inside of that element. Then we add event listenet for keydown event and suppres all keys. It would probably make sense to add some exceptions for some special keys like F11, F12, etc.

Full component code:


import React from 'react';
import ReactDOM from 'react-dom';
import Datetime from 'react-datetime';
import 'react-datetime/css/react-datetime.css';

export default class DateTimePicker extends React.Component {

    componentDidMount() {
      const componentPlaceholder = ReactDOM.findDOMNode(this);
      $(componentPlaceholder)
          .find('input')
          .on('keydown', () => false);
    }

    render() {
        return <Datetime {...this.props} />;
    }
}

Thursday, December 29, 2016

Unity and Quartz .NET

In my current project we need to use Quartz .NET for scheduled tasks. Since we already use Unity IoC it makes sense to inject all dependencies into quartz jobs. After googling I found this repository but example in that soulution as well as the nuget package keep throwing exceptions. Turned out to be that in order to integrate any IoC container with quartz it's enough to do three simple steps.

1. Implement IJobFactory interface

public class UnityJobFactory : IJobFactory
{
    private readonly IUnityContainer _container;

    public UnityJobFactory(IUnityContainer container)
    {
        _container = container;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        return _container.Resolve(bundle.JobDetail.JobType) as IJob;
    }

    public void ReturnJob(IJob job)
    {
    }
}

2. Inherit StdSchedulerFactory

public class UnitySchedulerFactory : StdSchedulerFactory
{
    private readonly IJobFactory _jobFactory;

    public UnitySchedulerFactory(IJobFactory jobFactory)
    {
        _jobFactory = jobFactory;
    }

    protected override IScheduler Instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs)
    {
        qs.JobFactory = _jobFactory;

        return base.Instantiate(rsrcs, qs);
    }
}

3. Register those two implementations in the container

container.RegisterType<IJobFactory, UnityJobFactory>();

container.RegisterType<ISchedulerFactory, UnitySchedulerFactory>();

In adition to keep everything more separated, independat and easier to use, we can extract registration into a Unity extension.

public class QuartzUnityExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        this.Container.RegisterType<IJobFactory, UnityJobFactory>();

        this.Container.RegisterType<ISchedulerFactory, UnitySchedulerFactory>();
    }
}

And of cause an example.


public class MyJob : IJob
{
    private readonly ISomeService _someService;

    public MyJob(ISomeService someService)
    {
        _someService = someService;
    }

    public void Execute(IJobExecutionContext context)
    {
        Console.WriteLine(_someService.GiveMeSomething());
    }
}

public interface ISomeService
{
    string GiveMeSomething();
}

public class SomeService : ISomeService
{
    public string GiveMeSomething()
    {
        return "Something";
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var container = new UnityContainer();

        container.AddNewExtension<QuartzUnityExtension>();

        container.RegisterType<ISomeService, SomeService>();

        var scheduler = container.Resolve<ISchedulerFactory>().GetScheduler();

        scheduler.ScheduleJob(
            new JobDetailImpl("myJob", typeof(MyJob)),
            new CalendarIntervalTriggerImpl("TestTrigger", IntervalUnit.Second, 2)
        );

        scheduler.Start();

        Thread.Sleep(TimeSpan.FromSeconds(10));

        scheduler.Shutdown();
    }
}

Friday, October 23, 2015

Nodejs 4.x.x on Raspberry Pi

Simple steps to install nodejs 4.x.x on Raspberry Pi 2:

sudo apt-get update

sudo apt-get upgrade

curl -sL https://deb.nodesource.com/setup | sudo bash -

sudo apt-get install nodejs

I got version 0.10.4 yours can be different.

Let's update it to the version 4.x.x:

sudo npm install -g n

sudo n stable

Restart the terminal and type

node -v

I got version v4.2.1

P.S. OS version on my Raspberry Pi:

Distributor ID: Debian
Description:    Debian GNU/Linux 7.8 (wheezy)
Release:        7.8
Codename:       wheezy

You can check your version simply by typing:

lsb_release -a

Tuesday, October 13, 2015

Nodejs 4.x.x on Debian 7 wheezy

If you use debian 7 wheezy and cannot install nodejs of version 4.x.x most likely it's because you have a wrong version of gcc. Nodejs 4.x.x requires gcc 4.8 or higher. Unfortunately Debian has gcc 4.8 starting from version 8 jessie. Fortunately there is a way to install it.

In order to install you need to adjust sources.list file in /etc/apt. Just add on line at the bottom of this file:

deb http://ftp.uk.debian.org/debian/ jessie main non-free contrib

Now check if you have preferences file in /etc/apt. If no then just create one.
Add following content to this file:

Package: *
Pin: release n=wheezy
Pin-Priority: 900

Package: gcc*
Pin: release n=jessie
Pin-Priority: 910

Now just use aptitude to install required version of gcc

sudo aptitude update
sudo aptitude install gcc/jessie