1
0
isundil 7 жил өмнө
parent
commit
d55c839180

+ 2 - 0
app/src/main/AndroidManifest.xml

@@ -5,6 +5,8 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
 
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+
     <application
         android:allowBackup="true"
         android:fullBackupContent="true"

+ 28 - 55
app/src/main/java/info/knacki/pass/git/GitSha1.java

@@ -2,15 +2,15 @@ package info.knacki.pass.git;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.logging.Logger;
 
-import info.knacki.pass.git.entities.GitCommit;
 import info.knacki.pass.git.entities.GitObject;
+import info.knacki.pass.git.entities.GitPackable;
+import info.knacki.pass.git.entities.GitPackableUtil;
 
 public class GitSha1 {
     public static byte[] StringToBytes(String hash) {
@@ -45,25 +45,24 @@ public class GitSha1 {
         try
         {
             MessageDigest shaHash = MessageDigest.getInstance("SHA1");
-            shaHash.update(("blob " +f.length()).getBytes());
-            shaHash.update(new byte[] {0});
-            InputStream input = new FileInputStream(f);
-            int numRead = 0;
-            while (numRead != -1)
-            {
-                byte[] buffer = new byte[1024];
-                numRead = input.read(buffer);
-                if (numRead > 0)
-                    shaHash.update(buffer, 0, numRead);
-            }
-            input.close();
-
-            return shaHash.digest();
+            byte[] pack = GitObject.GitBlob.GetFilePack(f);
+            int len = pack.length;
+            ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+            tmp.write(("blob " +len).getBytes());
+            tmp.write(new byte[] { 0 });
+            tmp.write(pack);
+            shaHash.update(tmp.toByteArray());
+            byte[] sha1sum = shaHash.digest();
+            Logger.getAnonymousLogger().severe("Sha1 for " +f.getAbsolutePath() +": " +GitSha1.BytesToString(sha1sum));
+            return sha1sum;
         }
         catch (FileNotFoundException e) {
             return new byte[]{};
         }
-        catch(Throwable t) {
+        catch(IOException t) {
+            t.printStackTrace();
+        }
+        catch (NoSuchAlgorithmException t) {
             t.printStackTrace();
         }
         return new byte[] {};
@@ -84,26 +83,6 @@ public class GitSha1 {
         return str.toByteArray();
     }
 
-    public static byte[] getTreeMsg(GitObject.GitTree tree) {
-        ByteArrayOutputStream str = new ByteArrayOutputStream();
-
-        try {
-            for (GitObject go : tree.GetObjects()) {
-                str.write((go.GetMode() + ' ').getBytes());
-                str.write(go.GetFilename().getBytes());
-                str.write(new byte[]{0});
-                str.write(go.GetHash());
-            }
-        }
-        catch (IOException e) {}
-        return str.toByteArray();
-    }
-
-
-    public static String getSha1OfObject(String rootPath, GitObject obj) {
-        return BytesToString(getRawSha1OfObject(rootPath, obj));
-    }
-
     private static byte[] getRawSha1OfTree(String rootPath, GitObject.GitTree tree) {
         try {
             MessageDigest sha1Builder = MessageDigest.getInstance("SHA1");
@@ -119,21 +98,6 @@ public class GitSha1 {
         }
     }
 
-    public static byte[] getRawSha1OfTreeNotRecursive(GitObject.GitTree tree) {
-        try {
-            MessageDigest sha1Builder = MessageDigest.getInstance("SHA1");
-            byte[] msg = getTreeMsg(tree);
-            sha1Builder.update(("tree " +msg.length).getBytes());
-            sha1Builder.update(new byte[] { 0 });
-            sha1Builder.update(msg);
-            byte[] digest = sha1Builder.digest();
-            return digest;
-        }
-        catch (NoSuchAlgorithmException e) {
-            return new byte[]{};
-        }
-    }
-
     private static byte[] getRawSha1OfBlob(String rootPath, GitObject.GitBlob blob) {
         return getRawSha1OfFile(rootPath, blob.GetGitPath());
     }
@@ -145,16 +109,25 @@ public class GitSha1 {
         return obj instanceof GitObject.GitTree ? getRawSha1OfTree(rootPath, (GitObject.GitTree) obj) : getRawSha1OfBlob(rootPath, (GitObject.GitBlob) obj);
     }
 
-    public static byte[] getCommitHash(GitCommit ci) {
+    public static byte[] getRawSha1OfPackable(GitPackable obj) {
         try {
             MessageDigest sha1Builder = MessageDigest.getInstance("SHA1");
-            sha1Builder.update(ci.getRawFormat().getBytes());
+            byte[] packContent = obj.GetPack();
+            sha1Builder.update((GitPackableUtil.GetObjTypeString(obj.GetPackableType()) +" " +packContent.length).getBytes());
+            sha1Builder.update(new byte[] { 0 });
+            sha1Builder.update(packContent);
             byte[] digest = sha1Builder.digest();
+            if (obj instanceof GitObject.GitBlob)
+                Logger.getLogger(GitSha1.class.getName()).severe("Coucou sha for " +new String(obj.GetPack()) +" is " +BytesToString(digest));
             return digest;
         }
         catch (NoSuchAlgorithmException e) {
             return new byte[]{};
         }
+    }
 
+    public static String getSha1OfPackable(GitPackable obj) {
+        byte[] raw = getRawSha1OfPackable(obj);
+        return raw.length == 0 ? "" : BytesToString(raw);
     }
 }

+ 41 - 35
app/src/main/java/info/knacki/pass/git/HttpGitProtocol.java

@@ -3,8 +3,6 @@ package info.knacki.pass.git;
 import android.os.AsyncTask;
 import android.util.Base64;
 
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -17,11 +15,11 @@ import java.net.URLConnection;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.SortedSet;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.zip.InflaterInputStream;
@@ -31,6 +29,7 @@ import info.knacki.pass.git.entities.GitObject;
 import info.knacki.pass.git.entities.GitPackable;
 import info.knacki.pass.git.entities.GitPackableUtil;
 import info.knacki.pass.git.entities.GitRef;
+import info.knacki.pass.io.OutputStreamWithCheckSum;
 import info.knacki.pass.settings.SettingsManager;
 
 public class HttpGitProtocol implements GitInterface { // FIXME package-private
@@ -135,7 +134,7 @@ public class HttpGitProtocol implements GitInterface { // FIXME package-private
             } catch (Throwable e) {
                 Map<String, List<String>> headers = in.getHeaderFields();
                 lp: for (List<String> i: headers.values()) {
-                    for (String status: (List<String>) i) {
+                    for (String status: i) {
                         log.severe(in.getURL().toString() + ": " + status);
                         break lp;
                     }
@@ -159,7 +158,7 @@ public class HttpGitProtocol implements GitInterface { // FIXME package-private
                     httpClient.setRequestProperty(header.getKey(), header.getValue());
                 }
                 if (fConfig.HasAuthentification()) {
-                    httpClient.setRequestProperty("Authorization", "basic " +Base64.encode("fConfig.GetUser():fConfig.GetPassword()".getBytes(), Base64.NO_WRAP));
+                    httpClient.setRequestProperty("Authorization", "basic " +Base64.encodeToString("fConfig.GetUser():fConfig.GetPassword()".getBytes(), Base64.NO_WRAP));
                 }
                 fOnReadyWrite.onResponse(httpClient.getOutputStream());
                 InputStream in = GetInputFilter(httpClient);
@@ -167,7 +166,7 @@ public class HttpGitProtocol implements GitInterface { // FIXME package-private
                 int totalRead = 0;
                 byte[] buffer = new byte[1024];
                 int currentRead;
-                while ((currentRead = in.read(buffer, 0, 1024)) != 0) {
+                while ((currentRead = in.read(buffer, 0, 1024)) > 0) {
                     fullBuffer.add(buffer);
                     buffer = new byte[1024];
                     totalRead += currentRead;
@@ -175,15 +174,19 @@ public class HttpGitProtocol implements GitInterface { // FIXME package-private
                         break;
                 }
                 in.close();
-                buffer = new byte[totalRead];
-                int i =0;
-                for (byte []currentBuf: fullBuffer) {
-                    for (int j =0; j < currentBuf.length && i*1024+j < totalRead; ++j) {
-                        buffer[i*1024 +j] = currentBuf[j];
+                if (totalRead >= 0) {
+                    buffer = new byte[totalRead];
+                    int i = 0;
+                    for (byte[] currentBuf : fullBuffer) {
+                        for (int j = 0; j < currentBuf.length && i * 1024 + j < totalRead; ++j) {
+                            buffer[i * 1024 + j] = currentBuf[j];
+                        }
+                        ++i;
                     }
-                    ++i;
+                    ManageResponse(buffer);
+                } else {
+                    ManageResponse(new byte[] {});
                 }
-                ManageResponse(buffer);
             }
             catch (IOException e) {
                 log.log(Level.WARNING, e.getMessage(), e);
@@ -290,7 +293,7 @@ public class HttpGitProtocol implements GitInterface { // FIXME package-private
         PullHash(ref.GetHash(), new OnResponseListener<byte[]>() {
             @Override
             public void onResponse(byte[] result) {
-                response.onResponse(new GitCommit(new String(result, Charset.defaultCharset())));
+                response.onResponse(new GitCommit(ref.GetHash(), new String(result, Charset.defaultCharset())));
             }
 
             @Override
@@ -446,17 +449,22 @@ public class HttpGitProtocol implements GitInterface { // FIXME package-private
         protoInflateGet(url, response);
     }
 
-    public byte[] makePack(List<GitPackable> objectsToPack) throws IOException
+    public boolean makePack(SortedSet<GitPackable> objectsToPack, OutputStream out) throws IOException
     {
-        ByteArrayOutputStream msg = new ByteArrayOutputStream();
-        msg.write("PACK".getBytes());
-        msg.write(new byte[] { 0, 0, 0, 2 } );
-        msg.write(ByteBuffer.allocate(4).putInt(objectsToPack.size()).array());
+        OutputStreamWithCheckSum msg = new OutputStreamWithCheckSum(out);
+        msg.write("PACK")
+            .write(new byte[] { 0, 0, 0, 2 } )
+            .write(ByteBuffer.allocate(4).putInt(objectsToPack.size()).array());
         for (GitPackable i: objectsToPack) {
             byte[] pack = i.GetPack();
-            byte[] header = GitPackableUtil.getObjHeader(i.GetPackableType(), 16711680);
+            if (null == pack)
+                return false;
+            msg.write(GitPackableUtil.getObjHeader(i.GetPackableType(), pack.length))
+                .write(GitPackableUtil.deflate(pack));
+            log.severe("Writing pack " +i.GetPackableType() +", " +GitSha1.getSha1OfPackable(i) +new String(i.GetPack()));
         }
-        return msg.toByteArray();
+        msg.writeSha1();
+        return true;
     }
 
     public void PushBlobs(final GitCommit.Builder commit, final GitInterface.OnStreamResponseListener<Void> response) {
@@ -470,37 +478,35 @@ public class HttpGitProtocol implements GitInterface { // FIXME package-private
                 }
                 if (myref != null) {
                     final GitRef finalRef = myref;
-                    response.onMsg("Pushing over " +myref.GetBranchName() +" revision " +myref.GetHash());
-                    ByteArrayOutputStream msg = new ByteArrayOutputStream();
+                    response.onMsg("Pushing over " +myref.GetBranch() +" revision " +myref.GetHash());
+                    log.severe("Pack: " +GitSha1.getSha1OfPackable(commit.Build()) +": " +new String(commit.Build().GetPack()));
                     try {
                         HashMap<String, String> headers = new HashMap<>();
                         headers.put("Content-Type", "application/x-git-receive-pack-request");
                         ProtoPost(new URL(fConfig.GetUrl() +"/git-receive-pack"), headers, new OnResponseListener<byte[]>() {
                             @Override
                             public void onResponse(byte[] result) {
-                                Logger.getAnonymousLogger().severe(new String(result, Charset.defaultCharset()));
+                                Logger.getAnonymousLogger().severe("git-receive-pack " +new String(result, Charset.defaultCharset()));
+                                response.onResponse(null);
                             }
 
                             @Override
                             public void onError(String msg, Throwable e) {
+                                Logger.getAnonymousLogger().severe("git-receive-pack ERROR");
                                 response.onError(msg, e);
                             }
                         }, new OnResponseListener<OutputStream>() {
                             @Override
                             public void onResponse(OutputStream result) {
-                                StringBuilder msgBuilder = new StringBuilder();
-                                GitCommit ci = commit.Build();
-                                byte[] ciRaw = ci.getRawFormat().getBytes();
-                                msgBuilder.append(finalRef.GetHash() + " " + GitSha1.BytesToString(GitSha1.getCommitHash(ci)) +" " +finalRef.GetBranchName());
-                                byte[] msgLine = msgBuilder.toString().getBytes();
+                                byte[] msgLine = (finalRef.GetHash() + " " + GitSha1.BytesToString(GitSha1.getRawSha1OfPackable(commit.Build())) +" " +finalRef.GetBranch()).getBytes();
                                 try {
-                                    result.write(String.format(Locale.US, "%04d", msgLine.length).getBytes());
+                                    result.write(String.format(Locale.US, "%04X", msgLine.length +4).getBytes());
                                     result.write(msgLine);
-                                    result.write("\n0000\n".getBytes());
-
-                                    result.write(String.format(Locale.US, "%04d", ciRaw.length).getBytes());
-                                    result.write(ciRaw);
-                                    result.write(makePack(commit.PreparePack()));
+                                    result.write("0000".getBytes());
+                                    if (!makePack(commit.PreparePack(), result)) {
+                                        onError("Pack error", null);
+                                        return;
+                                    }
                                 }
                                 catch (IOException e) {
                                     log.log(Level.SEVERE, "Cannot git-upload-pack: " +e.getMessage(), e);

+ 33 - 24
app/src/main/java/info/knacki/pass/git/entities/GitCommit.java

@@ -3,15 +3,19 @@ package info.knacki.pass.git.entities;
 import java.io.File;
 import java.text.SimpleDateFormat;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.Date;
-import java.util.List;
+import java.util.HashMap;
 import java.util.Locale;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.logging.Logger;
 
 import info.knacki.pass.git.GitSha1;
 
 public class GitCommit implements GitPackable {
-    private String fHash = null;
+    private final static Logger log = Logger.getLogger(GitCommit.class.getName());
+
+    private String fHash;
     private byte[] fTreeHash;
     private GitObject.GitTree fTree;
     private String fParent;
@@ -19,7 +23,7 @@ public class GitCommit implements GitPackable {
     private String fCommitter;
     private String fMessage;
 
-    public GitCommit(String sha1Content) {
+    public GitCommit(String hash, String sha1Content) {
         int pos = sha1Content.indexOf('\0');
 
         if (pos >= 0)
@@ -39,6 +43,7 @@ public class GitCommit implements GitPackable {
             else if (line.startsWith("committer"))
                 fCommitter = Util.RemoveHead(line);
         }
+        fHash = hash;
         fMessage = message == null ? "" : message.toString();
         fTree = null;
     }
@@ -48,6 +53,7 @@ public class GitCommit implements GitPackable {
         fCommitter = committer;
         fParent = parent.fHash;
         fMessage = msg;
+        fHash = null;
     }
 
     public String GetMessage() {
@@ -76,17 +82,6 @@ public class GitCommit implements GitPackable {
         return fParent;
     }
 
-    public String getRawFormat() {
-        Date date = new Date();
-        String dateStr = date.getTime() +" " +(new SimpleDateFormat("%Z", Locale.US).format(date));
-        return "tree " +GitSha1.BytesToString(fTree.GetHash()) +"\n" +
-                "parent " +GetParent() +"\n" +
-                "author " +fAuthor +" " +dateStr +"\n" +
-                "committer " +fAuthor +" " +dateStr +"\n" +
-                "\n" +
-                fMessage +"\n";
-    }
-
     @Override
     public eType GetPackableType() {
         return eType.eType_Commit;
@@ -94,8 +89,15 @@ public class GitCommit implements GitPackable {
 
     @Override
     public byte[] GetPack() {
-        // FIXME
-        return new byte[] {};
+        Date date = new Date();
+        String dateStr = (date.getTime() / 1000) +" " +(new SimpleDateFormat("Z", Locale.US).format(date));
+        String data = "tree " +GitSha1.BytesToString(fTree.GetHash()) +"\n" +
+                "parent " +GetParent() +"\n" +
+                "author " +fAuthor +" " +dateStr +"\n" +
+                "committer " +fCommitter +" " +dateStr +"\n" +
+                "\n" +
+                fMessage +"\n";
+        return data.getBytes();
     }
 
     public static class Builder {
@@ -103,9 +105,11 @@ public class GitCommit implements GitPackable {
         private final GitCommit fParent;
         private final GitObject.GitTree fTree;
         private final ArrayDeque<File> fFilesToPush = new ArrayDeque<>();
+        private final HashMap<String, GitPackable> fToPack = new HashMap<>();
 
-        public Builder(GitCommit parent, String author, String committer, String message) {
-            co = new GitCommit(parent, author, committer, message);
+        public Builder(GitCommit parent, String author, String authorEmail, String message) {
+            String aut = author +" <" +authorEmail +">";
+            co = new GitCommit(parent, aut, aut, message);
             fParent = parent;
             fTree = new GitObject.GitTree(parent.GetTree());
         }
@@ -117,7 +121,11 @@ public class GitCommit implements GitPackable {
 
         public GitCommit.Builder AddFile(String relativeFilename, File f) {
             fFilesToPush.add(f);
-            fTree.AddItem(relativeFilename, f);
+            GitObject obj = fTree.AddItem(relativeFilename, f);
+            do {
+                fToPack.put(obj.GetGitPath(), obj);
+                obj = obj.GetParent();
+            } while (obj != null);
             return this;
         }
 
@@ -131,10 +139,11 @@ public class GitCommit implements GitPackable {
             return this;
         }
 
-        public List<GitPackable> PreparePack() {
-            ArrayList<GitPackable> pack = new ArrayList<>();
-            pack.add(Build());
-            return pack;
+        public SortedSet<GitPackable> PreparePack() {
+            TreeSet packs = new TreeSet<GitPackable>(new GitPackableUtil.Comparator());
+            packs.add(Build());
+            packs.addAll(fToPack.values());
+            return packs;
         }
     }
 }

+ 65 - 14
app/src/main/java/info/knacki/pass/git/entities/GitObject.java

@@ -2,7 +2,12 @@ package info.knacki.pass.git.entities;
 
 import android.support.annotation.NonNull;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
@@ -24,8 +29,11 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
     }
 
     public static class GitBlob extends GitObject {
+        private File fFile;
+
         private GitBlob(GitTree parent, byte[] mode, String name, byte[] sha1) {
             super(parent, mode, name, sha1);
+            fFile = null;
         }
 
         private GitBlob(GitBlob copy) {
@@ -34,6 +42,7 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
 
         GitBlob(GitTree parent, String filename, File f) {
             this(parent, new byte[] { 49, 48, 48, 54, 52, 52 }, filename, GitSha1.getRawSha1OfFile(f));
+            fFile = f;
             Logger.getAnonymousLogger().severe("Sha1 of new file " +GetGitPath() +": " +GitSha1.BytesToString(fSha1));
         }
 
@@ -42,10 +51,34 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
             return eType.eType_Blob;
         }
 
+        public static byte[] GetFilePack(File f) throws IOException {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            InputStream input = new FileInputStream(f);
+            int numRead = 0;
+            while (numRead != -1)
+            {
+                byte[] buffer = new byte[1024];
+                numRead = input.read(buffer);
+                if (numRead > 0)
+                    out.write(buffer, 0, numRead);
+            }
+            input.close();
+            return out.toByteArray();
+        }
+
         @Override
         public byte[] GetPack() {
-            // FIXME
-            return new byte[] {};
+            try
+            {
+                return GetFilePack(fFile);
+            }
+            catch (FileNotFoundException e) {
+                return null;
+            }
+            catch(IOException t) {
+                t.printStackTrace();
+            }
+            return null;
         }
     }
 
@@ -161,29 +194,32 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
             return fParent == null;
         }
 
-        public GitTree AddItem(String path, File f) {
+        public GitObject AddItem(String path, File f) {
             if ('/' == path.charAt(0))
                 path = path.substring(1);
             final int nextSlash = path.indexOf('/');
             final String filename = nextSlash == -1 ? path : (path.substring(0, nextSlash));
             final GitObject obj = GetObject(filename);
+            final GitObject newItem;
 
             if (obj == null) {
                 if (nextSlash != -1) {
                     GitTree child = new GitTree(this, filename);
-                    child.AddItem(path.substring(nextSlash + 1), f);
+                    newItem = child.AddItem(path.substring(nextSlash + 1), f);
                 } else {
-                    fItems.put(filename, new GitBlob(this, filename, f));
+                    newItem = new GitBlob(this, filename, f);
+                    fItems.put(filename, newItem);
                 }
             } else if (nextSlash == -1) {
                 Remove(obj.fSha1);
-                fItems.put(filename, new GitBlob(this, filename, f));
+                newItem = new GitBlob(this, filename, f);
+                fItems.put(filename, newItem);
             } else {
-                ((GitTree) obj).AddItem(path.substring(nextSlash + 1), f);
+                newItem = ((GitTree) obj).AddItem(path.substring(nextSlash + 1), f);
             }
-            fSha1 = GitSha1.getRawSha1OfTreeNotRecursive(this);
+            fSha1 = GitSha1.getRawSha1OfPackable(this);
             Logger.getAnonymousLogger().severe("New sha for " +GetGitPath()+ ": " +GitSha1.BytesToString(fSha1));
-            return this;
+            return newItem;
         }
 
         public GitTree Remove(byte[] sha1) {
@@ -193,7 +229,7 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
                     break;
                 }
             }
-            fSha1 = GitSha1.getRawSha1OfTreeNotRecursive(this);
+            fSha1 = GitSha1.getRawSha1OfPackable(this);
             return this;
         }
 
@@ -204,8 +240,19 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
 
         @Override
         public byte[] GetPack() {
-            // FIXME
-            return new byte[] {};
+            ByteArrayOutputStream str = new ByteArrayOutputStream();
+
+            try {
+                for (GitObject go : GetObjects()) {
+                    str.write((go.GetMode() + ' ').getBytes());
+                    Logger.getLogger(GitObject.class.getName()).severe("out file in tree " +go.GetGitPath() +"$" + GitSha1.BytesToString(go.GetHash()));
+                    str.write(go.GetFilename().getBytes());
+                    str.write(new byte[]{0});
+                    str.write(go.GetHash());
+                }
+            }
+            catch (IOException e) {}
+            return str.toByteArray();
         }
     }
 
@@ -234,9 +281,9 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
     public String GetGitPath() {
         StringBuilder sb = new StringBuilder();
         GitTree parent = fParent;
-        sb.insert(0, "/" +fName);
+        sb.insert(0, fName);
         while (parent != null) {
-            sb.insert(0, "/" +parent.fName);
+            sb.insert(0, parent.fName + "/");
             parent = parent.fParent;
         }
         return sb.toString();
@@ -249,4 +296,8 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
     public String GetMode() {
         return new String(fMode);
     }
+
+    public int GetDepth() {
+        return fParent == null ? 1 : fParent.GetDepth() +1;
+    }
 }

+ 46 - 3
app/src/main/java/info/knacki/pass/git/entities/GitPackableUtil.java

@@ -1,7 +1,8 @@
 package info.knacki.pass.git.entities;
 
 import java.io.ByteArrayOutputStream;
-import java.util.logging.Logger;
+import java.io.IOException;
+import java.util.zip.DeflaterOutputStream;
 
 public class GitPackableUtil {
     public static byte getObjType(GitPackable.eType type) {
@@ -22,6 +23,18 @@ public class GitPackableUtil {
         return -1;
     }
 
+    public static String GetObjTypeString(GitPackable.eType type) {
+        switch (type) {
+            case eType_Commit:
+                return "commit";
+            case eType_Tree:
+                return "tree";
+            case eType_Blob:
+                return "blob";
+        }
+        return "";
+    }
+
     private static int bitToWrite(long len)
     {
         int nbBits = 0;
@@ -47,14 +60,44 @@ public class GitPackableUtil {
         while (nbBitToWrite > 0) {
             lastByte |= 0b10000000;
             buffer.write(lastByte);
-            Logger.getAnonymousLogger().severe(Integer.toBinaryString(lastByte & 0xFF).replace(' ', '0'));
             lastByte = 0;
             nbBitToWrite -= 7;
             lastByte |= (byte)(len & 0b1111111);
             len = len >> 7;
         }
         buffer.write(lastByte);
-        Logger.getAnonymousLogger().severe(Integer.toBinaryString(lastByte & 0xFF).replace(' ', '0'));
         return buffer.toByteArray();
     }
+
+    public static byte[] deflate(byte[] in) throws IOException {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        DeflaterOutputStream stream = new DeflaterOutputStream(output);
+        stream.write(in);
+        stream.finish();
+        stream.flush();
+        return output.toByteArray();
+    }
+
+    public static class Comparator implements java.util.Comparator<GitPackable> {
+        @Override
+        public int compare(GitPackable t1, GitPackable t2) {
+            if (t1.GetPackableType() == GitPackable.eType.eType_Blob && t2.GetPackableType() != GitPackable.eType.eType_Blob)
+                return 1;
+            if (t1.GetPackableType() != GitPackable.eType.eType_Blob && t2.GetPackableType() == GitPackable.eType.eType_Blob)
+                return -1;
+            if (t1.GetPackableType() == GitPackable.eType.eType_Blob && t2.GetPackableType() == GitPackable.eType.eType_Blob)
+                return ((GitObject.GitBlob) t1).GetFilename().compareTo(((GitObject.GitBlob) t2).GetFilename());
+            if (t1.GetPackableType() == GitPackable.eType.eType_Tree && t2.GetPackableType() != GitPackable.eType.eType_Tree)
+                return 1;
+            if (t1.GetPackableType() != GitPackable.eType.eType_Tree && t2.GetPackableType() == GitPackable.eType.eType_Tree)
+                return -1;
+            if (t1.GetPackableType() == GitPackable.eType.eType_Tree && t2.GetPackableType() == GitPackable.eType.eType_Tree)
+                return ((GitObject.GitTree) t1).GetDepth() -((GitObject.GitTree) t2).GetDepth();
+            if (t1.GetPackableType() == GitPackable.eType.eType_Commit)
+                return 1;
+            if (t2.GetPackableType() == GitPackable.eType.eType_Commit)
+                return -1;
+            return 0;
+        }
+    }
 }

+ 5 - 4
app/src/main/java/info/knacki/pass/input/InputService.java

@@ -44,12 +44,13 @@ public class InputService extends InputMethodService implements PasswordClickLis
         view.findViewById(R.id.prevButton).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
-                try {
-                    ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).showInputMethodPicker();
-                } catch (NullPointerException e) {
+                InputMethodManager service = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+                if (null == service) {
                     Toast.makeText(InputService.this, "Android error", Toast.LENGTH_LONG).show();
-                    log.log(Level.SEVERE, e.getMessage(), e);
+                    log.log(Level.SEVERE, "Cannot get Input method service");
+                    return;
                 }
+                service.showInputMethodPicker();
             }
         });
         view.findViewById(R.id.openAppButton).setOnClickListener(new View.OnClickListener() {

+ 48 - 0
app/src/main/java/info/knacki/pass/io/OutputStreamWithCheckSum.java

@@ -0,0 +1,48 @@
+package info.knacki.pass.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class OutputStreamWithCheckSum {
+    private static final Logger log = Logger.getLogger(OutputStreamWithCheckSum.class.getName());
+    private OutputStream fOutput;
+    private MessageDigest fDigest;
+
+    public OutputStreamWithCheckSum(OutputStream out) {
+        fOutput = out;
+        try {
+            fDigest = MessageDigest.getInstance("SHA1");
+        }
+        catch (NoSuchAlgorithmException e) {
+            log.log(Level.SEVERE, e.getMessage(), e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    public OutputStreamWithCheckSum write(byte b) throws IOException {
+        log.finer("Add 1 byte");
+        fDigest.update(b);
+        fOutput.write(new byte[] {b});
+        return this;
+    }
+
+    public OutputStreamWithCheckSum write(byte []b) throws IOException {
+        log.finer("Add " +b.length +" bytes");
+        fDigest.update(b);
+        fOutput.write(b);
+        return this;
+    }
+
+    public OutputStreamWithCheckSum write(String b) throws IOException {
+        return write(b.getBytes());
+    }
+
+    public OutputStreamWithCheckSum writeSha1() throws IOException {
+        fOutput.write(fDigest.digest());
+        return this;
+    }
+}

+ 13 - 11
app/src/main/java/info/knacki/pass/settings/ui/SettingsActivity.java

@@ -5,34 +5,34 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.preference.EditTextPreference;
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
 import android.preference.SwitchPreference;
+import android.support.v4.app.NavUtils;
 import android.support.v7.app.ActionBar;
-import android.preference.PreferenceFragment;
 import android.util.SparseArray;
 import android.view.MenuItem;
-import android.support.v4.app.NavUtils;
 import android.view.View;
 import android.widget.Toast;
 
+import java.io.File;
+import java.util.List;
+import java.util.logging.Logger;
+
 import info.knacki.pass.R;
 import info.knacki.pass.git.GitInterface;
 import info.knacki.pass.git.GitInterfaceFactory;
 import info.knacki.pass.git.entities.GitRef;
 import info.knacki.pass.io.PathUtils;
+import info.knacki.pass.settings.SettingsManager;
 import info.knacki.pass.ui.AlertText;
 import info.knacki.pass.ui.GitPullActivity;
-import info.knacki.pass.settings.SettingsManager;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.List;
-import java.util.logging.Logger;
 
 /**
  * A {@link PreferenceActivity} that presents a set of application settings. On
@@ -492,9 +492,11 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
         public void onActivityResult(int requestCode, int resultCode, Intent data) {
             if (requestCode == ACTIVITY_REQUEST_CODE_BROWSEGPG) {
                 if (resultCode == RESULT_OK) {
-                    String path = data.getData().getPath();
-                    Logger.getAnonymousLogger().severe("Coucou path {" +path +"}"); //FIXME
-                    SettingsManager.SetGPGKeyFile(getActivity(), path);
+                    Uri uriData = data.getData();
+                    if (null != uriData) {
+                        String path = uriData.getPath();
+                        SettingsManager.SetGPGKeyFile(getActivity(), path);
+                    }
                 }
             }
         }

+ 32 - 36
app/src/main/java/info/knacki/pass/ui/GitPullActivity.java

@@ -30,6 +30,8 @@ import info.knacki.pass.settings.SettingsManager;
 public class GitPullActivity extends AppCompatActivity {
     private final static Logger log = Logger.getLogger(GitPullActivity.class.getName());
 
+    public final static String COMMIT_MSG = "Coucou v2";//"Android pass sync";
+
     private GitInterface fGitInterfage;
     private GitCommit fHeadCommit;
 
@@ -138,7 +140,7 @@ public class GitPullActivity extends AppCompatActivity {
 
     void AskForConflicts(GitLocal localVersion, HashMap<String, GitObject.GitBlob> conflicts, HashMap<String, GitObject.GitBlob> filesToPull, Set<String> filesToPush) {
         // FIXME
-        filesToPull.putAll(conflicts);
+        filesToPush.addAll(conflicts.keySet());
         SyncFiles(localVersion, filesToPull, filesToPush);
     }
 
@@ -195,7 +197,7 @@ public class GitPullActivity extends AppCompatActivity {
             @Override
             public void onResponse(Void result) {
                 if (filesToPush.size() > 0)
-                    PushBlobs(filesToPush, allDone);
+                    PushBlobs(filesToPush, localVersion, allDone);
                 else
                     allDone.onResponse(result);
             }
@@ -295,42 +297,36 @@ public class GitPullActivity extends AppCompatActivity {
         localVersion.SetHash(filename, GitSha1.BytesToString(blob.GetHash()));
     }
 
-    void PushBlobs(final Set<String> files, final GitInterface.OnStreamResponseListener<Void> resp) {
-        SettingsManager.Git config = (SettingsManager.Git) SettingsManager.GetVCS(this);
-        GitCommit.Builder commit = new GitCommit.Builder(fHeadCommit, config.GetUsername(), config.GetUserEmail(), "Android pass sync");
-        for (String i: files)
-            commit.AddFile(i, new File(PathUtils.GetPassDir(this) +i));
-        fGitInterfage.PushBlobs(commit, new GitInterface.OnStreamResponseListener<Void>() {
-            @Override
-            public void onMsg(String message) {
-                Logger.getAnonymousLogger().severe(message);
-            }
+    void PushBlobs(final Set<String> files, final GitLocal localVersion, final GitInterface.OnStreamResponseListener<Void> resp) {
+        if (files.isEmpty()) {
+            resp.onMsg("Nothing to commit");
+            resp.onResponse(null);
+        } else {
+            SettingsManager.Git config = (SettingsManager.Git) SettingsManager.GetVCS(this);
+            final GitCommit.Builder commit = new GitCommit.Builder(fHeadCommit, config.GetUsername(), config.GetUserEmail(), COMMIT_MSG);
+            for (String i : files)
+                commit.AddFile(i, new File(PathUtils.GetPassDir(this) + i));
+            fGitInterfage.PushBlobs(commit, new GitInterface.OnStreamResponseListener<Void>() {
+                @Override
+                public void onMsg(String message) {
+                    resp.onMsg(message);
+                }
 
-            @Override
-            public void onResponse(Void result) {
-                Logger.getAnonymousLogger().severe("DONE");
-            }
+                @Override
+                public void onResponse(Void result) {
+                    final GitObject.GitTree tree = commit.Build().GetTree();
+                    for (String i: files) {
+                        localVersion.SetHash(i, GitSha1.BytesToString(tree.GetObjectFullPath(i).GetHash()));
+                    }
+                    onMsg("Done");
+                    resp.onResponse(result);
+                }
 
-            @Override
-            public void onError(String msg, Throwable e) {
-                Logger.getAnonymousLogger().log(Level.SEVERE, msg, e);
-            }
-        });
-        //resp.onResponse(null);
-        /*
-        GitPullActivity.this.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                ((AppCompatTextView)(findViewById(R.id.logView))).append("Committing " +blobs.size() +" files\n");
-                File[] files = new File[blobs.size()];
-                int i =0;
-                for (Map.Entry blob: blobs.entrySet()) {
-                    files[i++] = new File(PathUtils.GetPassDir(GitPullActivity.this) +blob.getKey());
+                @Override
+                public void onError(String msg, Throwable e) {
+                    Logger.getAnonymousLogger().log(Level.SEVERE, msg, e);
                 }
-                //    public void PushBlobs(final GitRef prev, final Map<String, GitObject.GitBlob> blobs, final File[] files, final GitInterface.OnResponseListener<Void> resp);
-                fGitInterfage.PushBlobs(PathUtils.GetPassDir(GitPullActivity.this), fTree, blobs, files, resp);
-            }
-        });
-        */
+            });
+        }
     }
 }

+ 1 - 2
app/src/main/java/info/knacki/pass/ui/MainActivity.java

@@ -21,7 +21,6 @@ import info.knacki.pass.R;
 import info.knacki.pass.generator.PasswordGenerator;
 import info.knacki.pass.generator.ui.PasswordGeneratorWizard;
 import info.knacki.pass.git.GitInterface;
-import info.knacki.pass.git.HttpGitProtocol;
 import info.knacki.pass.io.FileInterfaceFactory;
 import info.knacki.pass.io.IFileInterface;
 import info.knacki.pass.io.PathUtils;
@@ -134,7 +133,7 @@ public class MainActivity extends AppCompatActivity implements PasswordClickList
 
         requestPermissions();
 
-        //startActivity(new Intent(this, GitPullActivity.class));
+        startActivity(new Intent(this, GitPullActivity.class));
     }
 
     @Override