Thursday, 19 January 2017

Rewriting History with Git

Well, this is basically a followup of my previous blogpost about git.

A note of warning: rewriting history can be tricky, and you should perform this only on your local Git repository on things you haven't yet pushed to a remote (public) repository.

For more detailed information on how you can work with it, see the References. Right here, right now, I'm going to provide the way I've used it in my current work.

Logs

Rewriting history is done by "rebasing" your current checkins.

It helps if you can easily retrieve your current checkins from the logs.

The following shows my last 5 checkins in my local branch.
[mrbear@localhost project]$ git log --pretty=format:"%h %s" HEAD~5..HEAD
75d7620 BUGS-0010 Make it visible whether user is using the test or the production version.
ca9217e BUGS-0010 Cache test/prod urls.
b1f93c1 BUGS-0010 Replace hardcoded urls with configuration.
46908ce BUGS-0010 Implement configuration using Gulp.
49115c3 BUGS-0010 Two ways: "gulp test" or "gulp production".
Bear in mind that the log shows the checkins from most recent (on top) to the least recent (last). Rebasing takes the checkins in the opposite order.

Squashing checkins

Let us try to squash some commits that we've made together into one single commit.
[mrbear@localhost project]$ git rebase -i HEAD~5
49115c3 BUGS-0010 Two ways: "gulp test" or "gulp production".
46908ce BUGS-0010 Implement configuration using Gulp.
b1f93c1 BUGS-0010 Replace hardcoded urls with configuration.
ca9217e BUGS-0010 Cache test/prod urls.
75d7620 BUGS-0010 Make it visible whether user is using the test or the production version.

# Rebase d5defcb..75d7620 onto d5defcb (5 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
I've decided to squash 46908ce, so that this commit will be combined with its previous commit, the 49115c3.

I can even change the commit message and combine the two commit messages!
# This is a combination of 2 commits.
# The first commit's message is:

BUGS-0010 Implement configuration using Gulp.

# This is the 2nd commit message:

BUGS-0010 Two ways: "gulp test" or "gulp production".

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Thu Jan 19 14:13:50 2017 +0100
#
# rebase in progress; onto 0a98e03
# You are currently editing a commit while rebasing branch 'BUGS-0010' on '0a98e03'.
#
# Changes to be committed:
#       modified:   config.xml
#       modified:   lang.json
# Untracked files:
#       project/config.xml~
#       project/res/
#       project/www/conf/
#       project/www/i18n/
#
The output will look something like this:
[detached HEAD 5b2f880] BUGS-0010 Configuration using gulp in two ways: "gulp test" or "gulp production".
Date: Mon Jan 9 13:20:45 2017 +0100
8 files changed, 36 insertions(+), 6 deletions(-)
rename project/{ => conf}/config.xml (94%)
rename project/{www/i18n => conf}/lang.json (99%)
Successfully rebased and updated refs/heads/BUGS-0010.

Reordering checkins

Reordering the checkins, is as simple as cut&pasting the appropriate lines into a different sequence.

Removing checkins

Removing a checkin, can be done by just removing a line from the sequence. Use with caution.

References

Atlassian Git Tutorial - Rewriting history
https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase-i
Git - 7.6 Git Tools - Rewriting History
https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History

Thursday, 12 January 2017

Base 64 Encoding and URLs

I recently had some issues with base64 encoding of images and documents, prior to sending them over HTTP to the frontend of my app, and there decoding it again.

The issues were manifold.

Let me try and indicate the problems I encountered.

In the backend I use the BaseEncoding class provided in the Core of Google Guava. (com.google.common.io.BaseEncoding) The method "base64()"speaks for itself.

At the frontend, using Javascript, I used the "atob()" method1 to get the whole base64 encoded string back into it's original shape.

Problem 1 - String contains an invalid character

The "atob()" method threw an error, when attempting to decode String. After some research, involving comparing the string that is sent by the Backend, with the string received by the Frontend, I did notice two differences.

Apparently, the backend is sending a url-safe encoded string2, despite me not having specified that this is
what I want.

Well, the differences aren't major and a simple solution does the trick:
atob(contents.replace(/-/g, "+").replace(/_/g, "/"));
And voila, atob() no longer complains about invalid characters.

Problem 2 - Decoded document does not match original document

My app stored the received document, after decoding, into a file locally, so it can be opened. My App is created using Cordova (and Ionic and some other stuff) and I use the Cordova File Plugin3 to write the file.

The PDF document that I was using as a test, seemed to be transferred just fine, but upon opening it on the Tablet, an empty PDF document was shown.

There were vast differences between the original document and the decoded document. The differences seem to focus on the decidedly "weird" characters. The alphabet seemed just fine.

Being at a loss for the moment, I decided to use a library named js-base644. That didn't help in the slightest. Using runkit5 to test it, I found that it actually decodes base64 encoding into UTF-8 badly.

There were a number of bugs reported with it in GitHub.

Problem 3 - Cordova File Plugin

After switching back to the method "atob()", and comparing the output of "atob()" with the original document, I found them to be identical.

However, the file stored on the Tablet was still suffering from the exact same symptoms. Clearly something was going wrong with the Cordova File Plugin.

After looking at the documentation3, I found that the File Plugin will also output UTF-8, similar to the js-base64 library.

In the end, I found out that it only outputs UTF-8, if I write a string in a Blob to the File. If I change what I write into a JavaScript ArrayBuffer in a Blob, things work as they should.

And I finally got a nice PDF in the standard PDF Viewer of the Tablet.

References

[1] MDN - Base64 encoding and decoding
https://developer.mozilla.org/en/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
[2] RFC4648 The Base16, Base32, and Base64 Data Encodings - Section 5
https://tools.ietf.org/html/rfc4648#section-5
[3] File - Apache Cordova - cordova-plugin-file
https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/
[4] js-base64 - Yet another Base64 transcoder in pure JS
https://www.npmjs.com/package/js-base64
[5] Runkit
https://runkit.com/npm/js-base64
StackOverflow - Using Javascript's atob to decode base64 doesn't properly decode utf-8 strings
http://stackoverflow.com/questions/30106476/using-javascripts-atob-to-decode-base64-doesnt-properly-decode-utf-8-strings
C#411 - Convert Binary to Base64 String
http://www.csharp411.com/convert-binary-to-base64-string/