========================================== 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 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 ..." 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 ..." 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: 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 ..." 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.