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 cvsimport  command.

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

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

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

1$ git cvsimport -a -k -d:pserver:anonymous:[email protected]:/cvs/drupal-contrib -C drupify-copy contributions/themes/drupify
2Initialized empty Git repository in /Users/kevin/drupify-copy/.git/
3parse error on user@server in pserver
4cvs rlog: Logging contributions/themes/drupify
5cvs rlog: Logging contributions/themes/drupify/css
6cvs 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):

GitX showing the 17 commits imported from CVS, all by the upstream author, with the DRUPAL-6--1 and DRUPAL-5 tags marking the branch points

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

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

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

 1$ git show-ref
 24c33470f0f59bcfe7d0d88ee64945bb5625d6d02 refs/heads/DRUPAL-5
 38930672eaf97eefa8f9d4ed9f5144f466a97728f refs/heads/DRUPAL-6--1
 4e5907fac0160febbd91f0cda73633b3e6eafa2a9 refs/heads/master
 5e5907fac0160febbd91f0cda73633b3e6eafa2a9 refs/heads/origin
 6af9786625a280930b532541722806739e221ebda refs/original/refs/heads/DRUPAL-5
 7a9319cebb234c46cc8e0ada95ffb2cd81b87993c refs/original/refs/heads/DRUPAL-6--1
 8328f3440e202ed72253974dbbbd45f39db23ea4a refs/original/refs/heads/master
 9328f3440e202ed72253974dbbbd45f39db23ea4a refs/original/refs/heads/origin
10957bb22704bc8188c0421b68cbb2f52a3fdcdef6 refs/original/refs/tags/DRUPAL-6--1-0
11ee44c42250a2552c1dbef2f7165d65179e1d19c6 refs/tags/DRUPAL-6--1-0

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

The same repository after the branch filter, GitX now listing 34 commits because every one appears twice, once under its original refs/original ref

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

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

After the cleaning, references are back to normal:

1$ git show-ref
24c33470f0f59bcfe7d0d88ee64945bb5625d6d02 refs/heads/DRUPAL-5
38930672eaf97eefa8f9d4ed9f5144f466a97728f refs/heads/DRUPAL-6--1
4e5907fac0160febbd91f0cda73633b3e6eafa2a9 refs/heads/master
5e5907fac0160febbd91f0cda73633b3e6eafa2a9 refs/heads/origin
6ee44c42250a2552c1dbef2f7165d65179e1d19c6 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):

Commit detail after the folder move, with a red hand-drawn arrow and circle pointing at the rewritten path drupify-fork/page.tpl.php

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

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

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

 1$ cd kev-code/
 2$ git remote add drupify ../drupify-copy
 3$ git fetch drupify
 4warning: no common commits
 5remote: Counting objects: 106, done.
 6remote: Compressing objects: 100% (48/48), done.
 7remote: Total 106 (delta 40), reused 106 (delta 40)
 8Receiving objects: 100% (106/106), 61.62 KiB, done.
 9Resolving deltas: 100% (40/40), done.
10From ../drupify-copy
11 * [new branch]      DRUPAL-5   -> drupify/DRUPAL-5
12 * [new branch]      DRUPAL-6--1 -> drupify/DRUPAL-6--1
13 * [new branch]      master     -> drupify/master
14 * [new branch]      origin     -> drupify/origin
15From ../drupify-copy
16 * [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$ git remote show drupify
 2* remote drupify
 3  Fetch URL: ../drupify-copy
 4  Push  URL: ../drupify-copy
 5  HEAD branch (remote HEAD is ambiguous, may be one of the following):
 6    master
 7    origin
 8  Remote branches:
 9    DRUPAL-5    tracked
10    DRUPAL-6--1 tracked
11    master      tracked
12    origin      tracked
13  Local ref configured for 'git push':
14    master pushes to master (local out of date)

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

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

We can remove the attached drupify repository and its local drupify-copy source:

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

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

The kev-code repository after the merge, the imported CVS history threaded underneath my own initial commit and README

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$ git update-ref refs/heads/drupify/DRUPAL-6--1 8930672
2$ git update-ref refs/heads/drupify/master e5907fa
3$ git update-ref refs/heads/drupify/origin e5907fa
4$ git tag drupify/DRUPAL-6--1-0 DRUPAL-6--1-0
5$ git tag -d DRUPAL-6--1-0
6Deleted tag 'DRUPAL-6--1-0'

And finally, we can contemplate our work:

Final imported history with the upstream refs preserved as drupify/master, drupify/origin and the two DRUPAL tags alongside my own master

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-5 branch), to keep a perfect copy of the original CSV tree. But I failed to find a way. Does anyone have a clue?