==========================================
Distributed LedgerSMB Development With Git
==========================================

This document demonstrates how developer-integrators can work on
local LedgerSMB customization and help with upstream LedgerSMB
development using the distributed version control system, git.

git is useful in the following areas:

* Help with upstream development of LedgerSMB

* Make it easier to communicate send and receive bugfixes in
  prerelease/development versions of LedgerSMB

* Making necessary customizations that are unsuitable for
  upstream LedgerSMB

* Putting customer templates and graphics under version control

* Layering multiple customization branches to reproduce a
  configuration required for a particular customer deployment

* (One way of) putting the plaintext backup of the company
  database under version control.  At any time you can and do
  make snapshots, then examine the diff to see how each
  LedgerSMB operation affects the database tables.


Cloning the LedgerSMB Sourceforge Subversion Repository
=======================================================

In time we may have an official git mirror from which to clone
(a very quick copy operation), but you can make your own clone
of the LedgerSMB repository directly from Sourceforge:

  $ git svn -s clone https://ledger-smb.svn.sourceforge.net/svnroot/ledger-smb/ ledgersmb
  Initialized empty Git repository in /path/to/ledgersmb/.git/
  	A	ledger-smb/login.pl
  	A	ledger-smb/menu.ini
  	A	ledger-smb/ledger-smb.gif
  	A	ledger-smb/is.pl
  	A	ledger-smb/VERSION
  (..., ..., go get coffee)


An alternative method using rsync to bring down the entire
subversion repository (not just the working copy) can be used
for those experiencing sourceforge connection problems:

  $ rsync -avz rsync://ledger-smb.svn.sourceforge.net/svn/ledger-smb/* /path/to/ledger-smb.svn


Then clone from that local subversion repository URL, rewriting
the root to match the SourceForge repository:

  $ git svn clone --rewrite-root=https://ledger-smb.svn.sourceforge.net/svnroot/ledger-smb/ file://path/to/ledger-smb.svn ledgersmb


Fixing A Bug Using Git And Bugfix Branch
========================================

This section demonstrates fixing a trivial bug in LedgerSMB. The
method of communicating the patch to fix the bug differs
depending on whether you have commit priviledges to the
SourceForge repository or are using the SourceForge bug tracker
to send a patch.


Example: SourceForge Bug SF2046815
----------------------------------

(Note: The following commit was actually made by einhverfr)

During testing of LedgerSMB trunk, we found a simple bug in the
schema definition, and filed the report as SF2046815:

  ERROR: column e.control_code does not exist LINE 1:
  ..._ap_account_id, ec.cash_account_id, ec.threshold,
  e.control_... ^ QUERY: SELECT c.id, e.id, ec.entity_class,
  ec.discount, ec.taxincluded, ec.creditlimit, ec.terms,
  ec.meta_number, ec.business_id, ec.language_code,
  ec.pricegroup_id, ec.curr, ec.startdate, ec.enddate,
  ec.ar_ap_account_id, ec.cash_account_id, ec.threshold,
  e.control_code, ec.id FROM company c JOIN entity e
  ON (c.entity_id = e.id) JOIN entity_credit_account ec
  ON (c.entity_id = ec.entity_id) WHERE e.id = $1 AND
  ec.entity_class = CASE WHEN $2 = 3 THEN 2 WHEN $2 IS NULL THEN
  ec.entity_class ELSE $2 END CONTEXT: PL/pgSQL
  function "entity__list_credit" line 4 at FOR over SELECT rows


This is a one-liner fix, but it is still worth it to make a
branch, since fast branching and easy merging are strengths of
git.  The practice scales up well when you want to maintain
multiple branches and share them with others.

For those new to git: Here, we have the prompt set up to display
the current branch in parentheses.  If git is not currently on
any branch, the SHA1 name of the current HEAD will be displayed:

We intend our bugfix branch to be against the current trunk, so
we check that remote branch out.  It is important to use the
git-svn managed remotes only for branching purposes, never
commit to them.

  (1.2_jfkw) $ git checkout trunk
  Note: moving to "trunk" which isn't a local branch
  If you want to create a new branch from this checkout, 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 cb92467... Adding meta_number to company_billing_info return set


Update the branch to the latest revision before branching:

  (cb92467...) $ git svn rebase --all
  Current branch HEAD is up to date.


Now, we'll make a bugfix-branch, I prefer it to be named after
the SF bug:

  (cb92467...) $ git checkout -b 1.3_SF2046815_missing_entity_control_code
  Switched to a new branch "1.3_SF2046815_missing_entity_control_code"


You can use the git branch command to list all local branches.
For example, I'm keeping around all branches for bugs and
features I've worked on:

  $ git branch
    1.2_SF1877860_typo
    1.2_SF1928336_renames
    1.2_SF2013331_cookie_name
    1.2_SF2025931_till_equals_1
    1.2_cdog
    1.2_cdog_templates
    1.2_generate_salesorders_detail_default
    1.2_jfkw
    1.2_partnumber_like_space_delimiter
    1.2_print_pdf_default
    1.2_project_generate_orders
    1.2_receipts_on_invoice_screen
    1.2_require_customernumber
    1.2_require_customernumber_set_to_name
    1.2_sql_ins_customer_ins_project
    1.3_SF2013331_cookie_name
    1.3_SF2017284_smallgray_css
    1.3_SF2043569_install_doc
  * 1.3_SF2046815_missing_entity_control_code
    1.3_jfkw
    master


Again, note that the remote branches (trunk, 1.2) are seen with
the git branch -a command.  You should never modify those, just
think of them as read-only.  Git will keep track of the remote
origin branch your local branch came from.

Back to the bugfixing task at hand.  Edit the offending file:

  (1.3_SF2046815_missing_entity_control_code) $ emacsclient -n sql/Pg-database.sql


And in your editor, fix, save, test, etc.  When ready, view a
diff of the branch tip (HEAD) to your working copy:

  (1.3_SF2046815_missing_entity_control_code) $ git diff
  diff --git a/sql/Pg-database.sql b/sql/Pg-database.sql
  index e1d8251..5bf84c3 100644
  --- a/sql/Pg-database.sql
  +++ b/sql/Pg-database.sql
  @@ -19,6 +19,7 @@ CREATE index entity_class_idx ON entity_class(lower(class));

   CREATE TABLE entity (
     id serial UNIQUE,
  +  control_code text not null,
     name text check (name ~ '[[:alnum:]_]'),
     entity_class integer references entity_class(id) not null ,
     created date not null default current_date,


At this point you encounter something unique about git.  You
have an intermediary layer between your working copy and HEAD,
called the index.  The index has been described variously as
'the next commit'.

Use the git status command to see what has changed in your
working copy:

  (1.3_SF2046815_missing_entity_control_code) $ git status
  # On branch 1.3_SF2046815_missing_entity_control_code
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
  #
  #       modified:   sql/Pg-database.sql
  #
  no changes added to commit (use "git add" and/or "git commit -a")


And decide which files among these, in whole or just in part
should be added to the index, which is to be the next commit.

In this case, there's only one file changed, so you can add it
to the next local commit by filename, or by adding all under the
current directory (e.g. '.'):

  (1.3_SF2046815_missing_entity_control_code) $ git add .


Check the status now, where you see that file added to the index
is designated 'to be committed'.

  (1.3_SF2046815_missing_entity_control_code) $ git status
  # On branch 1.3_SF2046815_missing_entity_control_code
  # Changes to be committed:
  #   (use "git reset HEAD <file>..." to unstage)
  #
  #       modified:   sql/Pg-database.sql
  #


If you want to review the diff again once the index has been
updated, use git diff --cached.


Commiting Your Changes Locally
------------------------------

The above git status output looks good, we're ready to commit
locally:

  (1.3_SF2046815_missing_entity_control_code) $ git commit -m 'add missing entity.control_code column'
  Created commit a3503cb: add missing entity.control_code column
   1 files changed, 1 insertions(+), 0 deletions(-)


Commiters: Push Upstream With dcommit
-------------------------------------

If you have commit rights to the SourceForge repository, you can
git svn dcommit your local commits directly.

Update your bugfix branch with git svn rebase first:

  (1.3_SF2046815_missing_entity_control_code) $ git svn rebase
  Current branch 1.3_SF2046815_missing_entity_control_code is up to date.


Now, you can git svn dcommit one or more commits on this branch
which are not yet in the upstream subversion repository:

  (1.3_SF2046815_missing_entity_control_code) $ git svn dcommit
  Committing to https://ledger-smb.svn.sourceforge.net/svnroot/ledger-smb/trunk ...
  Authentication realm: <https://ledger-smb.svn.sourceforge.net:443> SourceForge Subversion area
  Username: einhverfr
  Password for 'einhverfr':
  	M	INSTALL
  Committed r2253
  	M	INSTALL
  r2253 = dcc0556bd0e1db3a4fc0788f2e60b62fa0c3378d (trunk)
  No changes between current HEAD and refs/remotes/trunk
  Resetting to the latest refs/remotes/trunk

(Note: The above upstream commit is simulated for the purposes
of this tutorial)


Or, Generate A Patch
--------------------

If you don't have commit rights, and you're simply patching a
bug from the tracker, make a diff of your mature/completed bug
branch to its origin branch appropriate documentation:

  (1.3_SF2046815_missing_entity_control_code) $ git diff trunk > 1.3_SF2046815_missing_entity_control_code.diff


And upload 1.3_SF2046815_missing_entity_control_code.diff as a
patch with appropriate documentation.


Example: Removing a File
------------------------

Dubbed 'the stupid content tracker', git can guess at content
changes to track renames.  However, git handles the usual
operations of add, delete, rename to best and most predictable
effect if you use the git {mv,rm} form to explicitly tell git
the intent of your layout changes.

In this example, we are removing the outdated README.svn-status
file.  We don't need to make a branch for this small change.

Rebase your current remote-tracking branch to the latest
revision:

  (1.3_jfkw) $ git svn rebase
  Current branch 1.3_jfkw is up to date.


Delete the file:

  (1.3_jfkw) $ git rm README.svn-status
  rm 'README.svn-status'


This updates the index.  Now examine 'your next commmit':

  (1.3_jfkw) $ git status
  # On branch 1.3_jfkw
  # Changes to be committed:
  #   (use "git reset HEAD <file>..." to unstage)
  #
  #	deleted:    README.svn-status


Looks good, commit the change:

  (1.3_jfkw) $ git commit -m 'remove outdated README.svn-status'
  Created commit 855a980: remove outdated README.svn-status
   1 files changed, 0 insertions(+), 3 deletions(-)
   delete mode 100644 README.svn-status


Here we find that git svn dcommit does the right thing with this
kind of commit, too:

  (1.3_jfkw) $ git svn dcommit
  Committing to https://ledger-smb.svn.sourceforge.net/svnroot/ledger-smb/trunk ...
  	D	README.svn-status
  Committed r2314
  	D	README.svn-status
  r2314 = 93d75d87fb9300045f53ee3c8a585b2ecc402e4f (trunk)
  No changes between current HEAD and refs/remotes/trunk
  Resetting to the latest refs/remotes/trunk


Note that we weren't asked for the username and password again.

For a reality check using a subversion checkout, this changeset
appears indistinguishable from one made with subversion itself.

  $ svn up /path/to/svn/ledgersmb13
  D    /path/to/svn/ledgersmb13/README.svn-status
  Updated to revision 2314.

  $ svn log --limit 3 /path/to/svn/ledgersmb13
  --------------------------------------------------------------------
  r2314 | jfkw | 2008-09-04 18:12:12 -0400 (Thu, 04 Sep 2008) | 1 line

  remove outdated README.svn-status
  --------------------------------------------------------------------


DVCS Participation Encouraged
=============================

The LedgerSMB developers welcome contributions of code and
real-world testing.  DVCS can be an effective way to conserve
developer time, the LedgerSMB community's most limited resource.

Developing with DVCS such as git will make it easier to
communicate about LedgerSMB code. If you are doing work with
LedgerSMB, DVCS makes it easier to review and if appropriate, to
merge your mature feature branch upstream.

Future additions to this document may include using a git
hosting service such as gitorious or github.  With a shared git
repository you can share your branches with others who may want
to collaborate or test.  Best of all you can ask the LedgerSMB
committers to pull from your repository when your branch is
mature enough to be accepted upstream.

If you're interested in using DVCS and git in particular to
contribute to LedgerSMB, The author is available on #ledgersmb
as jfkw or on the mailing list ledgersmb-dev.