Faculty of Information Technology
Software Engineering Group

Creating and Submitting Patch to Smalltalk/X

Sometimes something goes wrong - there's a bug. Bugs are inevitable, code is written by human beings and we all make mistakes, no matter how thoroughly we test. But bugs should be fixed - and the nice thing on Smalltalk is that it's easy to fix or improve the system.

This tutorial shows step-by-step how to create submit a patch to Smalltalk/X jv-branch.

Introduction

In the following text let's use issue #111 as a model bug we'd like to fix. It's a bug, but it's an easy one to fix. The aim of this text is to show what to do once you figure out what's wrong and managed to fix the code so the easiest possible bug will do.

In following text I'll use word "patch" and "fix" interchangeably.

Going back to issue #111, it's not too difficult that the problem is in DisplaySurface >> #fillDeviceRectangleWithPattern:x:y:width:height:patternOffset:, line ~92 where it sends unimplemented method #deviceClippingBoundsOrNil:. This is a leftover from refactoring or a like - it should send #deviceClippingBounds: instead. So let's change the code:

So we have fixed the bug and now we're ready to actually create abd submit patch.

Actually, there are more ways how to send a patch (i) "a preferred way" (let's call it so) and (ii) "the easy way". The "preferred way" is maybe a little more complicated but I (JV) as maintainer would prefer that since it's little less work for me, preserves your name and allows to split fix in multiple commits which is important if fix is bigger (as it helps in eventual bisections later on). So let's start with the "preferred way"

The Preferred Way

The preferred way assumes you are able to compile Smalltalk/X jv-branch yourself. You may want to check a section on Building Smalltalk/X jv-branch from Sources first.

Another thing is that it requires that code you modified is actually stored in Mercurial (some packages which have not yet been forked are checked out from eXept's CVS - yes, that's right, eXept's using CVS even in 2017). To check whether class/method is stored in Mercurial repository, simply select the class / method and look to browser status bar - it shows SCM type and revision:

If it shows something like "HG 7841:3e74422a72dd", you're fine, it's Mercurial.

Basically, to create a patch, you need to:

  • commit the code to Mercurial repository,
  • test it on a fresh build
  • export commit(s) using hg export,
  • attach commit to issue in bug tracker or send it to mailing list

That's it. Now, let's look at individual steps in detail.

Step 1: Commit

Switch browser to package view (pick ViewPackage menu item) and then for each modified package, select the package, then modified classes and then commit. Pick ClassesMercurial+ (default)CheckIn... menu. Depending on your preferences it could also be ClassesRepository Mercurial+CheckIn... but in either case, the menu item should have text Mercurial+ in it. If you don't see this, something's wrong with your configuration. You may have a look at Smalltalk/X Mercurial Documentation.

In the commit dialog, fill in a commit message. A lot has been written on how to write a good commit messages, but at least:

  • if there's a bug report for it, the first line with "Issue #<issue number>:"
  • explain what you have done and why it fixes the bug
  • if there's a bug report for it, add a line at the end of commit message with link to the bug.

Note: When you want to commit via command line on MS Windows you can use hg commit command which will open a notepad instance. After finishing the comment you save it and close the notepad.

Step 2: Recompiling and Testing

Once patch is commited, it's important to recompile patched code and test it once more from a fresh compilation. This is to make sure commited patch is complete and is working on a clean environment - sometimes you may forget to commit some code or code might fail to compile for one reason or another.

So, quit the IDE, open a terminal. First, check that code has been commited:

jv@WIN7 H:\Projects\SmalltalkX\sources\jv1_x64_win\build\stx
> cd libview

jv@WIN7 H:\Projects\SmalltalkX\sources\jv1_x64_win\build\stx\libview
> hg log -r "grep('#111')"
changeset:   7843:8c70a4ba1cf2
branch:      jv
tag:         tip
user:        Jan Vrany <jan.vrany@fit.cvut.cz>
date:        Tue Jan 10 16:53:12 2017 +0000
summary:     Issue #111: Send `#deviceClippingBounds:` instead of `#deviceClippingBoundsOrNil:`

Good, the code is commited in repository, the commit ID (changeset ID) is, in this case, 8c70a4ba1cf2. So update the working copy to that revision:

jv@WIN7 H:\Projects\SmalltalkX\sources\jv1_x64_win\build\stx\libview                                              
> hg up -r 8c70a4ba1cf2                                                                                           
1 files updated, 0 files merged, 0 files removed, 0 files unresolved                                              
                                                                                                                  
jv@WIN7 H:\Projects\SmalltalkX\sources\jv1_x64_win\build\stx\libview                                              
> hg sum                                                                                                          
parent: 7843:8c70a4ba1cf2 tip                                                                                     
 Issue #111: Send `#deviceClippingBounds:` instead of `#deviceClippingBoundsOrNil:`                               
branch: jv                                                                                                        
commit: 74 unknown (clean)                                                                                        
update: 1 new changesets, 2 branch heads (merge)                                                                  
phases: 10 draft                                                                                                  

Here we use hg sum afterwards to double-check the working copy is updated as desired. We have to update working copy manually as committing code from Smalltalk/X IDE does not (yet) update it[*].

Now, as we're sure we have current revision checked out, recompile whole Smalltalk/X:

jv@WIN7 H:\Projects\SmalltalkX\sources\jv1_x64_win\build\stx\libview                                              
> rake
...an awfull lot of compilation spew, then...
cd -                                                                                          
cd -                                                                                          
rm H:/Projects/SmalltalkX/sources/jv1_x64_win/build/stx/projects/smalltalk/modules.stx        
cd H:/Projects/SmalltalkX/sources/jv1_x64_win/build/stx/projects/smalltalk                    
OKay, VM runs                                                                                 
cd -                                                                                          

If compilation succeeds (it should) then start a fresh smalltalk and test the fix. If everything's OK, proceed to next step. If not, see section "Amending Already Commited Patch".

Step 3: Export and Submit the Patch

Once patch is tested, the last step is to export the patch from your repository. Use hg export to do that, like:

jv@WIN7 H:\Projects\SmalltalkX\sources\jv1_x64_win\build\stx\libview   
> hg export -o "issue_111_patch_%n_of_%N_r%h.patch" -r 8c70a4ba1cf2    

This would create patch file issue_111_patch_1_of_1_r8c70a4ba1cf2.patch - this file should be attached to a bug report (#111 in our example) or sent to mailing list.

Congrats - you have just submitted a patch!

Testing Patches Submitted by Others

If you report a bug and don't fix it yourself, you may be asked to test a patch created by the above procedure.

First make sure you have all changes committed otherwise you will get an abort message abort: uncommitted changes

A tip: to view uncommitted changes you can do

hg diff > view_changes.txt

Second you have to be in the directory where the patched file:

For example take a patch libtool_fix_1_of_2_rev_091022874048_Issue_99__Added_tests_catching_issue_99.patch from ticket #99:

  1. Change to correct directory:
    cd C:\prg_sdk\Stx-jv_branch_build\stx-jv\build\stx\libtool\
    
    where you find the file that needs to be patched: Tools__CodeView2.st
  1. Apply the patch:
    C:\prg_sdk\Stx-jv_branch_build\stx-jv\build\stx\libtool>hg import --no-commit c:\prg_sdk\Stx-jv_branch_build\stx-jv\build\patches\libtool_fix_2_of_2_rev_7c788008841c_Issue_99__Send_notification_to_services_also_from__basicWithoutRedraw___.patch
    

    Note: Please note the option --no-commit when importing patch(es). It only updates the working copy but does not commit.

    If changes were committed and the patch proved to be wrong or it would need further improvement, then you'd be left with some "old" commits. You would then need to get rid of such "old" commits via hg prune.

    By using --no-commit option you save yourself the tedious work. You just need to run hg revert to dispose of such patch(es).

    If you really want patches to be committed, please use hg import --exact.

    Note2: Please note that if you want to test multiple patches, and you don't have the same patches on the same day, (e.g. on Windows) with --no-commit it has to be done differently than --no-commit on subsequent steps.

    The first step is, of course, natural hg import --no-commit name1.patch.

    During the second step you have use patch.exe. It can be patched like this: patch -p1 < name2.patch (works only at cmd.exe!). The patch.exe is from git e.g. patch: /c/Program Files (x86)/Git/bin/patch.exe) and with that one it works.

    If you would like to use powershell then an option is to use cat name2.patch | patch -p1.

    If you have all the patches at the same time you can always do hg import --no-commit name1.patch name2.patch

  1. Check if the patch is there (will not be shown when --no-commit is used)
    C:\prg_sdk\Stx-jv_branch_build\stx-jv\build\stx\libtool>hg sum
    parent: 17208:3e2915fd01ac tip
    Issue 99: Send notification to services also from #basicWithoutRedraw...
    branch: jv
    commit: 190 unknown (clean)
    update: (current)
    phases: 1 draft
    
    This is correct last patch is for the issue #99
  1. Then you have to recompile:
    rake compile
    
  1. Test whether the patch actually fixes the problem for you and report back to patch developer.

Further Comments

Amending Already Committed Patch

It may happen the patch does not work on fresh system for one reason or another or you may be asked to refactor it, add more comments, etc. In that case, start your Smalltalk with previous patch compiled in ,as described in section "Step 2: Recompiling and Testing", make your changes and commit again. This time with a ticked "Amend" check box in commit dialog:

If you don't see the "Amend" check box you have to check your Mercurial configuration.

Steps to check your configuration:

<HERE TO HAVE LINK TO mercurial configuration (not to have same information twice - it is much harder to maintain)>

1) Check if your you have the check box

Use Shared repositories (EXPERIMENTAL) checked

(StX must be restarted after saving the configuration) <SCREENSHOT REQUIRED>

2) Check your Mercurial (hg) configuration file and it must contain:

[extensions]
  share = 

Note: When you want to commit via command line on MS Windows you can use hg commit --amend command which will open a notepad instance with the previous text already filled out. After finishing the comment you save it and close the notepad.

Patches Spread Among Multiple Packages

Sometimes to fix a bug or otherwise improve the system, you need to change multiple packages. In that case, repeat steps 1 to 3 for each changed package.

Packages Containing Many Changes

Sometimes to fix a bug one has to change a lot of code - then the patch would be quite big, with a lot of changes here and there. In that case it's a very good thing split changes in several subsequent commits. This makes it a lot easier to review and, eventually, bisect if something turns out to be wrong with it (this happens, like it or not - we're imperfect humans).

If patch consists of several commits, simply export all of them like:

hg export -o "issue_111_patch_%n_of_%N_r%h.patch" -r <id of first commit>::<id of last commit>

The Easy Way

The easy way is to just save changes you made in running system to a file (called changeset in Smalltalk world). There are many ways to do it, but the safest option is to save a session changeset:

  1. Start a ChangeSetBrowser2 which will show all the changes main in current session. In Workspace, "Do It" following:
    Tools::ChangeSetBrowser2 open
    
    A changeser browser should appear:
  1. There you have to open session changeset (menu "File" => "Open Session Changeset"). This gives you an list of all changes for your current session.

  1. Then you can check the checkbox to indicate the the changes you want to save.

  1. To save the changes use menu "File" => "Save as...". (will save only checked changes)
  1. Attach saved file to bug report.

Alternatively, if it's just one of few methods in one class, you mey do it right from the browser:

Testing Patches Submitted by Others

To be written


[*] The reason is to keep source code in sync - compiled code keeps references only to file names and offsets to save memory. If file changes menawhile, offsets become out-of-sync and browser would show wrong code.

Last modified 3 years ago Last modified on Apr 24, 2018, 12:32:26 PM

Attachments (9)

Download all attachments as: .zip