Ubercart instead of Magento or osCommerce.

from... http://kevin.deldycke.com/tag/drupal/

 

Why I chose Ubercart instead of Magento or osCommerce.

 

About a year ago a friend asked me why I chose the Ubercart/Drupal combo as the engine for Cool Cavemen’s online shop. He specifically asked me why not choose Magento or osCommerce. I never really took the time to answer him. Let’s fix this.

First, I selected a couple of web eCommerce solution based on the same requirements upon which I choose WordPress some years ago. Namely:

  • Open-source free software
  • Active project
  • Healthy community

I excluded osCommerce right away. I quickly played with it in 2005 and in 2007 for projects that never came to fruition. And I keep a really bad memory of these experiences: both code and templates felt poorly designed and implemented. That’s why osCommerce is classified in my brain as a giant legacy code base, hard to maintain. Maybe the project has evolved a lot since then. I should have looked back at it, but was attracted by another project…

…which was Magento. So yes, I seriously considered to use Magento at some point. It looked great. And clean. But digging deeper I found that something was missing.

My shop was supposed to sell tee-shirts for the band. And a tee-shirt is a product that can have lots of variations:

  • different colors (white, black, yellow, …),
  • of different sizes (S, M, L, X, XL, …),
  • in different shapes (teeshirt, hoodie, sweatshirt, …),
  • for different peoples (guy, girl, baby, …).

I wanted to track stocks and prices for each combination. And this degree of complexity is not supported by Magento. To be fair, I remember to have heard of this feature, but was only available in a third party module (that I don’t remember the name). Alas, this module was proprietary. That is the main reason I discarded Magento.

And then I found Ubercart, which allowed me to do exactly what I wanted. Here is for example prices and stocks for a black Cool Cavemen tee-shirt:

I use Ubercart since 2008 and I’m really happy with it. The future is interesting, as Ubercart was forked as the Drupal Commerce project a year ago. I’m waiting for the 1.0 release of the latter to decide if it is worth switching to the fork or not. But having it based on Drupal 7 is good news, as Ubertcart still stick to the (old) Drupal 6.

 

 

How-to fork a CVS project with Git

 

This week I’ve decided to put my work on Cool Cavemen’s concert videos aside, and work instead on refreshing our online store. After all, fans are requesting this, so I can’t escape my duty…

The theme the store is based on is Drupify, an adaptation of the RokWebify Joomla theme. All these themes are licensed under the GPL, so I have to share all my modifications with the community. This is a great opportunity to seriously experiment with Git (at last !).

Here is my plan:

  1. Make an exact copy of Drupify’s code base in my GitHub repository.
  2. Hack it in this playground.
  3. ???
  4. Profit ! :D

Problem: Drupify lives in a CVS repository.

Solution: Git features a cvsimportcommand.

Before going further, we need to install cvsps. For MacPorts users, this is as simple as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
~$ sudo port install cvsps
Password:
--->  Computing dependencies for cvsps
--->  Fetching cvsps
--->  Attempting to fetch cvsps-2.1.tar.gz from http://arn.se.distfiles.macports.org/cvsps
--->  Attempting to fetch cvsps-2.1.tar.gz from http://distfiles.macports.org/cvsps
--->  Verifying checksum(s) for cvsps
--->  Extracting cvsps
--->  Applying patches to cvsps
--->  Configuring cvsps
--->  Building cvsps
--->  Staging cvsps into destroot
--->  Installing cvsps @2.1_1
--->  Activating cvsps @2.1_1
--->  Cleaning cvsps

Then we create a temporary copy of Drupify’s CVS repository:

1
2
3
4
5
6
~$ git cvsimport -a -k -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal-contrib -C drupify-copy contributions/themes/drupify
Initialized empty Git repository in /Users/kevin/drupify-copy/.git/
parse error on user@server in pserver
cvs rlog: Logging contributions/themes/drupify
cvs rlog: Logging contributions/themes/drupify/css
cvs rlog: Logging contributions/themes/drupify/images

The new Git repository automatically created is named drupify-copy. Here is how it looks like in GitX (notice tags and branches):

To keep things clean and tidy, I want to relocate all the content of this repository to a drupify-forkfolder. Inspired by Pedro Melo, we’ll use the git
filter-branch
to do this job:

1
2
3
4
5
6
7
8
9
10
11
12
~$ cd drupify-copy
drupify-copy$ git filter-branch -f --prune-empty --tree-filter '
  mkdir -p /tmp/drupify-fork;
  mv $(ls -A) /tmp/drupify-fork;
  mv /tmp/drupify-fork drupify-fork
' -- --all
Rewrite a9319cebb234c46cc8e0ada95ffb2cd81b87993c (17/17)
Ref 'refs/heads/DRUPAL-5' was rewritten
Ref 'refs/heads/DRUPAL-6--1' was rewritten
Ref 'refs/heads/master' was rewritten
Ref 'refs/heads/origin' was rewritten
Ref 'refs/tags/DRUPAL-6--1-0' was rewritten

The command we just used alter all the commits, in a way that let Drupify act as if it was located, since the beginning of its history, in the drupify-forksub-directory.

By default, filter-branchcreates a backup of the tree using references prefixed by refs/original/:

1
2
3
4
5
6
7
8
9
10
11
drupify-copy$ git show-ref
4c33470f0f59bcfe7d0d88ee64945bb5625d6d02 refs/heads/DRUPAL-5
8930672eaf97eefa8f9d4ed9f5144f466a97728f refs/heads/DRUPAL-6--1
e5907fac0160febbd91f0cda73633b3e6eafa2a9 refs/heads/master
e5907fac0160febbd91f0cda73633b3e6eafa2a9 refs/heads/origin
af9786625a280930b532541722806739e221ebda refs/original/refs/heads/DRUPAL-5
a9319cebb234c46cc8e0ada95ffb2cd81b87993c refs/original/refs/heads/DRUPAL-6--1
328f3440e202ed72253974dbbbd45f39db23ea4a refs/original/refs/heads/master
328f3440e202ed72253974dbbbd45f39db23ea4a refs/original/refs/heads/origin
957bb22704bc8188c0421b68cbb2f52a3fdcdef6 refs/original/refs/tags/DRUPAL-6--1-0
ee44c42250a2552c1dbef2f7165d65179e1d19c6 refs/tags/DRUPAL-6--1-0

We’re not the only ones to not see through this mess. GitX seems to be confused too:

But according Jakub Narębski on the Git mailing-list, we can safely removes Git’s backups:

1
2
3
4
5
6
7
drupify-copy$ rm -Rf .git/refs/original
drupify-copy$ git gc --prune=now
Counting objects: 106, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (88/88), done.
Writing objects: 100% (106/106), done.
Total 106 (delta 40), reused 0 (delta 0)

After the cleaning, references are back to normal:

1
2
3
4
5
6
drupify-copy$ git show-ref
4c33470f0f59bcfe7d0d88ee64945bb5625d6d02 refs/heads/DRUPAL-5
8930672eaf97eefa8f9d4ed9f5144f466a97728f refs/heads/DRUPAL-6--1
e5907fac0160febbd91f0cda73633b3e6eafa2a9 refs/heads/master
e5907fac0160febbd91f0cda73633b3e6eafa2a9 refs/heads/origin
ee44c42250a2552c1dbef2f7165d65179e1d19c6 refs/tags/DRUPAL-6--1-0

We can then fire up GitX to get the ultimate proof that the relocation operation didn’t change anything, but the base folder (and SHA hashes):

It’s time to import all this code in our main repository. First, get a local copy of our public GitHub code base:

1
2
3
4
5
6
7
drupify-copy$ cd
~$ git clone git@github.com:kdeldycke/kev-code.git
Initialized empty Git repository in /Users/kevin/kev-code/.git/
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 5 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (5/5), done.

Now let’s include our temporary drupify-copyas a tracked remote repository:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
~$ cd kev-code/
kev-code$ git remote add drupify ../drupify-copy
kev-code$ git fetch drupify
warning: no common commits
remote: Counting objects: 106, done.
remote: Compressing objects: 100% (48/48), done.
remote: Total 106 (delta 40), reused 106 (delta 40)
Receiving objects: 100% (106/106), 61.62 KiB, done.
Resolving deltas: 100% (40/40), done.
From ../drupify-copy
 * [new branch]      DRUPAL-5   -> drupify/DRUPAL-5
 * [new branch]      DRUPAL-6--1 -> drupify/DRUPAL-6--1
 * [new branch]      master     -> drupify/master
 * [new branch]      origin     -> drupify/origin
From ../drupify-copy
 * [new tag]         DRUPAL-6--1-0 -> DRUPAL-6--1-0

As you can see, all the little particularities of the remote repository are well tracked (HEAD, branches and tags are there):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kev-code$ git remote show drupify
* remote drupify
  Fetch URL: ../drupify-copy
  Push  URL: ../drupify-copy
  HEAD branch (remote HEAD is ambiguous, may be one of the following):
    master
    origin
  Remote branches:
    DRUPAL-5    tracked
    DRUPAL-6--1 tracked
    master      tracked
    origin      tracked
  Local ref configured for 'git push':
    master pushes to master (local out of date)

Another way to check this is to list all tracked remote branches:

1
2
3
4
5
6
kev-code$ git branch -r
  drupify/DRUPAL-5
  drupify/DRUPAL-6--1
  drupify/master
  drupify/origin
  origin/HEAD -> origin/master

It’s time to merge all our tracked remote code (from drupify-copy) in our local repository (kev-code). The branch I’m interested in is DRUPAL-6--1, as it holds the latest Drupify code for Drupal 6.x:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
kev-code$ git merge drupify/DRUPAL-6--1
Merge made by recursive.
 drupify-fork/README.txt               |   16 +
 drupify-fork/css/editor_content.css   |    7 +
 drupify-fork/css/index.html           |    1 +
 drupify-fork/css/template_ie.css      |   55 +++
 drupify-fork/drupify.info             |   11 +
 drupify-fork/images/arrow.png         |  Bin 0 -> 278 bytes
 drupify-fork/images/bg.png            |  Bin 0 -> 315 bytes
 drupify-fork/images/bottom-bg.png     |  Bin 0 -> 583 bytes
 drupify-fork/images/col-divider.png   |  Bin 0 -> 200 bytes
 drupify-fork/images/emailButton.png   |  Bin 0 -> 454 bytes
 drupify-fork/images/favicon.ico       |  Bin 0 -> 1150 bytes
 drupify-fork/images/footer-bg.png     |  Bin 0 -> 544 bytes
 drupify-fork/images/footer-l.png      |  Bin 0 -> 593 bytes
 drupify-fork/images/footer-r.png      |  Bin 0 -> 592 bytes
 drupify-fork/images/footer-rocket.png |  Bin 0 -> 3391 bytes
 drupify-fork/images/header-bg.png     |  Bin 0 -> 638 bytes
 drupify-fork/images/header-l.png      |  Bin 0 -> 430 bytes
 drupify-fork/images/header-r.png      |  Bin 0 -> 444 bytes
 drupify-fork/images/indent1.png       |  Bin 0 -> 214 bytes
 drupify-fork/images/indent2.png       |  Bin 0 -> 214 bytes
 drupify-fork/images/indent3.png       |  Bin 0 -> 214 bytes
 drupify-fork/images/indent4.png       |  Bin 0 -> 214 bytes
 drupify-fork/images/index.html        |    1 +
 drupify-fork/images/menu-bg.png       |  Bin 0 -> 343 bytes
 drupify-fork/images/menu-bullet.png   |  Bin 0 -> 933 bytes
 drupify-fork/images/menu-divider.png  |  Bin 0 -> 200 bytes
 drupify-fork/images/pdf_button.png    |  Bin 0 -> 482 bytes
 drupify-fork/images/printButton.png   |  Bin 0 -> 467 bytes
 drupify-fork/logo.png                 |  Bin 0 -> 27829 bytes
 drupify-fork/node.tpl.php             |   31 ++
 drupify-fork/page.tpl.php             |  173 ++++++++++
 drupify-fork/screenshot.png           |  Bin 0 -> 11289 bytes
 drupify-fork/style.css                |  593 +++++++++++++++++++++++++++++++++
 drupify-fork/template.php             |   10 +
 34 files changed, 898 insertions(+), 0 deletions(-)
 create mode 100644 drupify-fork/README.txt
 create mode 100644 drupify-fork/css/editor_content.css
 create mode 100644 drupify-fork/css/index.html
 create mode 100644 drupify-fork/css/template_ie.css
 create mode 100644 drupify-fork/drupify.info
 create mode 100644 drupify-fork/images/arrow.png
 create mode 100644 drupify-fork/images/bg.png
 create mode 100644 drupify-fork/images/bottom-bg.png
 create mode 100644 drupify-fork/images/col-divider.png
 create mode 100644 drupify-fork/images/emailButton.png
 create mode 100644 drupify-fork/images/favicon.ico
 create mode 100644 drupify-fork/images/footer-bg.png
 create mode 100644 drupify-fork/images/footer-l.png
 create mode 100644 drupify-fork/images/footer-r.png
 create mode 100644 drupify-fork/images/footer-rocket.png
 create mode 100644 drupify-fork/images/header-bg.png
 create mode 100644 drupify-fork/images/header-l.png
 create mode 100644 drupify-fork/images/header-r.png
 create mode 100644 drupify-fork/images/indent1.png
 create mode 100644 drupify-fork/images/indent2.png
 create mode 100644 drupify-fork/images/indent3.png
 create mode 100644 drupify-fork/images/indent4.png
 create mode 100644 drupify-fork/images/index.html
 create mode 100644 drupify-fork/images/menu-bg.png
 create mode 100644 drupify-fork/images/menu-bullet.png
 create mode 100644 drupify-fork/images/menu-divider.png
 create mode 100644 drupify-fork/images/pdf_button.png
 create mode 100644 drupify-fork/images/printButton.png
 create mode 100644 drupify-fork/logo.png
 create mode 100644 drupify-fork/node.tpl.php
 create mode 100644 drupify-fork/page.tpl.php
 create mode 100644 drupify-fork/screenshot.png
 create mode 100644 drupify-fork/style.css
 create mode 100644 drupify-fork/template.php

We can remove the attached drupifyrepository and its local drupify-copysource:

1
2
3
kev-code$ git remote rm drupify
kev-code$ cd ..
~$ rm -rf ./drupify-copy

At this stage, here is what our repository looks like:

To keep all the details that were created by git cvsimport, we can add by hand all the missing refs. The only difference with the original ones is that I unified the namespace with a drupify/prefix:

1
2
3
4
5
6
kev-code$ git update-ref refs/heads/drupify/DRUPAL-6--1 8930672
kev-code$ git update-ref refs/heads/drupify/master e5907fa
kev-code$ git update-ref refs/heads/drupify/origin e5907fa
kev-code$ git tag drupify/DRUPAL-6--1-0 DRUPAL-6--1-0
kev-code$ git tag -d DRUPAL-6--1-0
Deleted tag 'DRUPAL-6--1-0'

And finally, we can contemplate our work:

This let me work cleanly on the CVS branch I wanted to in the first place. But there is one missing thing: all other tracked remote branches were not merged properly. I really wanted to import all of them (especially the DRUPAL-5branch), to keep a perfect copy of the original CSV tree. But I failed to find a way. Does anyone have a clue ?