{"action":"created","comment":{"url":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/comments/28646501","id":28646501,"diff_hunk":"@@ -155,12 +198,41 @@ static void _hashOfFile(unsigned char* hash, FTSENT *ent)\n     return @(hexHash);\n }\n \n-void removeTree(NSString *path)\n+extern NSString *hashOfTree(NSString *path)\n {\n-    [[NSFileManager defaultManager] removeItemAtPath:path error:nil];\n+    return hashOfTreeWithVersion(path, LATEST_DELTA_DIFF_MAJOR_VERSION);\n }\n \n-void copyTree(NSString *source, NSString *dest)\n+BOOL removeTree(NSString *path)\n {\n-    [[NSFileManager defaultManager] copyItemAtPath:source toPath:dest error:nil];\n+    NSFileManager *fileManager = [NSFileManager defaultManager];\n+    // Don't use fileExistsForPath: because it will try to follow symbolic links\n+    if (![fileManager attributesOfItemAtPath:path error:nil]) {\n+        return YES;\n+    }\n+    return [fileManager removeItemAtPath:path error:nil];\n+}\n+\n+BOOL copyTree(NSString *source, NSString *dest)\n+{\n+    return [[NSFileManager defaultManager] copyItemAtPath:source toPath:dest error:nil];\n+}\n+\n+BOOL modifyPermissions(NSString *path, mode_t desiredPermissions)\n+{\n+    NSFileManager *fileManager = [NSFileManager defaultManager];\n+    NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:nil];\n+    if (!attributes) {\n+        return NO;\n+    }\n+    NSNumber *permissions = attributes[NSFilePosixPermissions];\n+    if (!permissions) {\n+        return NO;\n+    }\n+    mode_t newMode = ([permissions unsignedShortValue] & ~PERMISSION_FLAGS) | desiredPermissions;","path":"Sparkle/SUBinaryDeltaCommon.m","position":202,"original_position":202,"commit_id":"cd478a4c484c61d4ccdf9c307c9ca047b434ae21","original_commit_id":"cd478a4c484c61d4ccdf9c307c9ca047b434ae21","user":{"login":"pornel","id":72159,"avatar_url":"https://avatars.githubusercontent.com/u/72159?v=3","gravatar_id":"","url":"https://api.github.com/users/pornel","html_url":"https://github.com/pornel","followers_url":"https://api.github.com/users/pornel/followers","following_url":"https://api.github.com/users/pornel/following{/other_user}","gists_url":"https://api.github.com/users/pornel/gists{/gist_id}","starred_url":"https://api.github.com/users/pornel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pornel/subscriptions","organizations_url":"https://api.github.com/users/pornel/orgs","repos_url":"https://api.github.com/users/pornel/repos","events_url":"https://api.github.com/users/pornel/events{/privacy}","received_events_url":"https://api.github.com/users/pornel/received_events","type":"User","site_admin":false},"body":"Ah, right. I've misread `~`.","created_at":"2015-04-18T18:59:24Z","updated_at":"2015-04-18T18:59:24Z","html_url":"https://github.com/sparkle-project/Sparkle/pull/524#discussion_r28646501","pull_request_url":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/524","_links":{"self":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/comments/28646501"},"html":{"href":"https://github.com/sparkle-project/Sparkle/pull/524#discussion_r28646501"},"pull_request":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/524"}}},"pull_request":{"url":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/524","id":33592589,"html_url":"https://github.com/sparkle-project/Sparkle/pull/524","diff_url":"https://github.com/sparkle-project/Sparkle/pull/524.diff","patch_url":"https://github.com/sparkle-project/Sparkle/pull/524.patch","issue_url":"https://api.github.com/repos/sparkle-project/Sparkle/issues/524","number":524,"state":"open","locked":false,"title":"Binary delta Improvements","user":{"login":"zorgiepoo","id":857267,"avatar_url":"https://avatars.githubusercontent.com/u/857267?v=3","gravatar_id":"","url":"https://api.github.com/users/zorgiepoo","html_url":"https://github.com/zorgiepoo","followers_url":"https://api.github.com/users/zorgiepoo/followers","following_url":"https://api.github.com/users/zorgiepoo/following{/other_user}","gists_url":"https://api.github.com/users/zorgiepoo/gists{/gist_id}","starred_url":"https://api.github.com/users/zorgiepoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zorgiepoo/subscriptions","organizations_url":"https://api.github.com/users/zorgiepoo/orgs","repos_url":"https://api.github.com/users/zorgiepoo/repos","events_url":"https://api.github.com/users/zorgiepoo/events{/privacy}","received_events_url":"https://api.github.com/users/zorgiepoo/received_events","type":"User","site_admin":false},"body":"OK, been working on this for a bit.\r\n\r\nI found several bugs with BinaryDelta's tree hashing, and added the ability for BinaryDelta to diff/preserve permissions. I also created major/minor version fields in the file format. If the major field differs, this indicates the patch not being compatible. When the minor version differs, compatibility still works. Due to having to add permission diffs and having to change the hashing, I had to bump the major version.\r\n\r\nChanges summary:\r\n* Permissions are preserved and hashed on regular files, symbolic links, and directories now in all cases. Only permission modes 0755 and 0644 are allowed on files in the after-tree. Practical use: scripts turning into executable or vise versa, .app being compressed incorrectly with wrong permissions, fixing permissions from an old app in a new update...\r\n* ACLs (Access Control Lists) are not allowed in either before-tree or after-tree; we don't try to preserve or hash them, and we don't want to diff on them. Extended file attributes are not hashed and completely ignored as they shouldn't contain vital information.\r\n* _DARWIN_NO_64_BIT_INODE define was removed because I believe it's no longer needed since we only target 64-bit now.\r\n* Directories are now hashed. They were intended to be by the original code since _hashOfFileContents has a case for them. This was a bug.\r\n* File types (regular file, directory, symbolic link) are now hashed. This prevents hash collisions for two different trees in a few cases.\r\n* More error handling is now done. Cleaned up code a bit by adding constants. Added a lot of tests.\r\n* Added major/minor version fields to the patch format. Bumped the (major) patch version to 2. Patches created by BinaryDelta now will not work on older versions unless you create a patch by supplying --version=1; this is meant for transition purposes. BinaryDelta can still apply version 1 patches. Since major/minor versioning wasn't around before, for version 2 patches I had to change the before and after hash property keys to make the patch gracefully fail on older versions of BinaryDelta.\r\n\r\nAfter this, I'm thinking of adding a verbose option and improve the current logging.","created_at":"2015-04-18T00:23:47Z","updated_at":"2015-04-18T18:59:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6a7731ebc268e0ed7c0ce3e60a65b43a8a842972","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/524/commits","review_comments_url":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/524/comments","review_comment_url":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sparkle-project/Sparkle/issues/524/comments","statuses_url":"https://api.github.com/repos/sparkle-project/Sparkle/statuses/cd478a4c484c61d4ccdf9c307c9ca047b434ae21","head":{"label":"zorgiepoo:binary-delta-permissions","ref":"binary-delta-permissions","sha":"cd478a4c484c61d4ccdf9c307c9ca047b434ae21","user":{"login":"zorgiepoo","id":857267,"avatar_url":"https://avatars.githubusercontent.com/u/857267?v=3","gravatar_id":"","url":"https://api.github.com/users/zorgiepoo","html_url":"https://github.com/zorgiepoo","followers_url":"https://api.github.com/users/zorgiepoo/followers","following_url":"https://api.github.com/users/zorgiepoo/following{/other_user}","gists_url":"https://api.github.com/users/zorgiepoo/gists{/gist_id}","starred_url":"https://api.github.com/users/zorgiepoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zorgiepoo/subscriptions","organizations_url":"https://api.github.com/users/zorgiepoo/orgs","repos_url":"https://api.github.com/users/zorgiepoo/repos","events_url":"https://api.github.com/users/zorgiepoo/events{/privacy}","received_events_url":"https://api.github.com/users/zorgiepoo/received_events","type":"User","site_admin":false},"repo":{"id":33382254,"name":"Sparkle","full_name":"zorgiepoo/Sparkle","owner":{"login":"zorgiepoo","id":857267,"avatar_url":"https://avatars.githubusercontent.com/u/857267?v=3","gravatar_id":"","url":"https://api.github.com/users/zorgiepoo","html_url":"https://github.com/zorgiepoo","followers_url":"https://api.github.com/users/zorgiepoo/followers","following_url":"https://api.github.com/users/zorgiepoo/following{/other_user}","gists_url":"https://api.github.com/users/zorgiepoo/gists{/gist_id}","starred_url":"https://api.github.com/users/zorgiepoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zorgiepoo/subscriptions","organizations_url":"https://api.github.com/users/zorgiepoo/orgs","repos_url":"https://api.github.com/users/zorgiepoo/repos","events_url":"https://api.github.com/users/zorgiepoo/events{/privacy}","received_events_url":"https://api.github.com/users/zorgiepoo/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/zorgiepoo/Sparkle","description":"A software update framework for OS X","fork":true,"url":"https://api.github.com/repos/zorgiepoo/Sparkle","forks_url":"https://api.github.com/repos/zorgiepoo/Sparkle/forks","keys_url":"https://api.github.com/repos/zorgiepoo/Sparkle/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zorgiepoo/Sparkle/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zorgiepoo/Sparkle/teams","hooks_url":"https://api.github.com/repos/zorgiepoo/Sparkle/hooks","issue_events_url":"https://api.github.com/repos/zorgiepoo/Sparkle/issues/events{/number}","events_url":"https://api.github.com/repos/zorgiepoo/Sparkle/events","assignees_url":"https://api.github.com/repos/zorgiepoo/Sparkle/assignees{/user}","branches_url":"https://api.github.com/repos/zorgiepoo/Sparkle/branches{/branch}","tags_url":"https://api.github.com/repos/zorgiepoo/Sparkle/tags","blobs_url":"https://api.github.com/repos/zorgiepoo/Sparkle/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zorgiepoo/Sparkle/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zorgiepoo/Sparkle/git/refs{/sha}","trees_url":"https://api.github.com/repos/zorgiepoo/Sparkle/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zorgiepoo/Sparkle/statuses/{sha}","languages_url":"https://api.github.com/repos/zorgiepoo/Sparkle/languages","stargazers_url":"https://api.github.com/repos/zorgiepoo/Sparkle/stargazers","contributors_url":"https://api.github.com/repos/zorgiepoo/Sparkle/contributors","subscribers_url":"https://api.github.com/repos/zorgiepoo/Sparkle/subscribers","subscription_url":"https://api.github.com/repos/zorgiepoo/Sparkle/subscription","commits_url":"https://api.github.com/repos/zorgiepoo/Sparkle/commits{/sha}","git_commits_url":"https://api.github.com/repos/zorgiepoo/Sparkle/git/commits{/sha}","comments_url":"https://api.github.com/repos/zorgiepoo/Sparkle/comments{/number}","issue_comment_url":"https://api.github.com/repos/zorgiepoo/Sparkle/issues/comments{/number}","contents_url":"https://api.github.com/repos/zorgiepoo/Sparkle/contents/{+path}","compare_url":"https://api.github.com/repos/zorgiepoo/Sparkle/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zorgiepoo/Sparkle/merges","archive_url":"https://api.github.com/repos/zorgiepoo/Sparkle/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zorgiepoo/Sparkle/downloads","issues_url":"https://api.github.com/repos/zorgiepoo/Sparkle/issues{/number}","pulls_url":"https://api.github.com/repos/zorgiepoo/Sparkle/pulls{/number}","milestones_url":"https://api.github.com/repos/zorgiepoo/Sparkle/milestones{/number}","notifications_url":"https://api.github.com/repos/zorgiepoo/Sparkle/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zorgiepoo/Sparkle/labels{/name}","releases_url":"https://api.github.com/repos/zorgiepoo/Sparkle/releases{/id}","created_at":"2015-04-03T20:50:34Z","updated_at":"2015-04-11T16:29:02Z","pushed_at":"2015-04-18T17:25:36Z","git_url":"git://github.com/zorgiepoo/Sparkle.git","ssh_url":"git@github.com:zorgiepoo/Sparkle.git","clone_url":"https://github.com/zorgiepoo/Sparkle.git","svn_url":"https://github.com/zorgiepoo/Sparkle","homepage":"http://sparkle-project.org","size":11324,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"sparkle-project:master","ref":"master","sha":"2ca0a739e5d18c5365b7943df9fb18a3a7ea85cf","user":{"login":"sparkle-project","id":7999168,"avatar_url":"https://avatars.githubusercontent.com/u/7999168?v=3","gravatar_id":"","url":"https://api.github.com/users/sparkle-project","html_url":"https://github.com/sparkle-project","followers_url":"https://api.github.com/users/sparkle-project/followers","following_url":"https://api.github.com/users/sparkle-project/following{/other_user}","gists_url":"https://api.github.com/users/sparkle-project/gists{/gist_id}","starred_url":"https://api.github.com/users/sparkle-project/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sparkle-project/subscriptions","organizations_url":"https://api.github.com/users/sparkle-project/orgs","repos_url":"https://api.github.com/users/sparkle-project/repos","events_url":"https://api.github.com/users/sparkle-project/events{/privacy}","received_events_url":"https://api.github.com/users/sparkle-project/received_events","type":"Organization","site_admin":false},"repo":{"id":274782,"name":"Sparkle","full_name":"sparkle-project/Sparkle","owner":{"login":"sparkle-project","id":7999168,"avatar_url":"https://avatars.githubusercontent.com/u/7999168?v=3","gravatar_id":"","url":"https://api.github.com/users/sparkle-project","html_url":"https://github.com/sparkle-project","followers_url":"https://api.github.com/users/sparkle-project/followers","following_url":"https://api.github.com/users/sparkle-project/following{/other_user}","gists_url":"https://api.github.com/users/sparkle-project/gists{/gist_id}","starred_url":"https://api.github.com/users/sparkle-project/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sparkle-project/subscriptions","organizations_url":"https://api.github.com/users/sparkle-project/orgs","repos_url":"https://api.github.com/users/sparkle-project/repos","events_url":"https://api.github.com/users/sparkle-project/events{/privacy}","received_events_url":"https://api.github.com/users/sparkle-project/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sparkle-project/Sparkle","description":"A software update framework for OS X","fork":false,"url":"https://api.github.com/repos/sparkle-project/Sparkle","forks_url":"https://api.github.com/repos/sparkle-project/Sparkle/forks","keys_url":"https://api.github.com/repos/sparkle-project/Sparkle/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sparkle-project/Sparkle/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sparkle-project/Sparkle/teams","hooks_url":"https://api.github.com/repos/sparkle-project/Sparkle/hooks","issue_events_url":"https://api.github.com/repos/sparkle-project/Sparkle/issues/events{/number}","events_url":"https://api.github.com/repos/sparkle-project/Sparkle/events","assignees_url":"https://api.github.com/repos/sparkle-project/Sparkle/assignees{/user}","branches_url":"https://api.github.com/repos/sparkle-project/Sparkle/branches{/branch}","tags_url":"https://api.github.com/repos/sparkle-project/Sparkle/tags","blobs_url":"https://api.github.com/repos/sparkle-project/Sparkle/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sparkle-project/Sparkle/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sparkle-project/Sparkle/git/refs{/sha}","trees_url":"https://api.github.com/repos/sparkle-project/Sparkle/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sparkle-project/Sparkle/statuses/{sha}","languages_url":"https://api.github.com/repos/sparkle-project/Sparkle/languages","stargazers_url":"https://api.github.com/repos/sparkle-project/Sparkle/stargazers","contributors_url":"https://api.github.com/repos/sparkle-project/Sparkle/contributors","subscribers_url":"https://api.github.com/repos/sparkle-project/Sparkle/subscribers","subscription_url":"https://api.github.com/repos/sparkle-project/Sparkle/subscription","commits_url":"https://api.github.com/repos/sparkle-project/Sparkle/commits{/sha}","git_commits_url":"https://api.github.com/repos/sparkle-project/Sparkle/git/commits{/sha}","comments_url":"https://api.github.com/repos/sparkle-project/Sparkle/comments{/number}","issue_comment_url":"https://api.github.com/repos/sparkle-project/Sparkle/issues/comments{/number}","contents_url":"https://api.github.com/repos/sparkle-project/Sparkle/contents/{+path}","compare_url":"https://api.github.com/repos/sparkle-project/Sparkle/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sparkle-project/Sparkle/merges","archive_url":"https://api.github.com/repos/sparkle-project/Sparkle/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sparkle-project/Sparkle/downloads","issues_url":"https://api.github.com/repos/sparkle-project/Sparkle/issues{/number}","pulls_url":"https://api.github.com/repos/sparkle-project/Sparkle/pulls{/number}","milestones_url":"https://api.github.com/repos/sparkle-project/Sparkle/milestones{/number}","notifications_url":"https://api.github.com/repos/sparkle-project/Sparkle/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sparkle-project/Sparkle/labels{/name}","releases_url":"https://api.github.com/repos/sparkle-project/Sparkle/releases{/id}","created_at":"2009-08-11T07:05:25Z","updated_at":"2015-04-18T17:47:28Z","pushed_at":"2015-04-18T17:47:28Z","git_url":"git://github.com/sparkle-project/Sparkle.git","ssh_url":"git@github.com:sparkle-project/Sparkle.git","clone_url":"https://github.com/sparkle-project/Sparkle.git","svn_url":"https://github.com/sparkle-project/Sparkle","homepage":"http://sparkle-project.org","size":13611,"stargazers_count":1914,"watchers_count":1914,"language":"C","has_issues":true,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":477,"mirror_url":null,"open_issues_count":142,"forks":477,"open_issues":142,"watchers":1914,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/524"},"html":{"href":"https://github.com/sparkle-project/Sparkle/pull/524"},"issue":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/issues/524"},"comments":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/issues/524/comments"},"review_comments":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/524/comments"},"review_comment":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/pulls/524/commits"},"statuses":{"href":"https://api.github.com/repos/sparkle-project/Sparkle/statuses/cd478a4c484c61d4ccdf9c307c9ca047b434ae21"}}}}