gitgithubgrit

How does Github allow for inline file editing? (Or how to add or edit files in a bare git repository)


I have a small application that manages several git repositories similar to Github/Gitorious. Github allows for inline file editing, and I'd like to know if anyone has any idea on how they manage this.

My initial thought was that it would do a complete clone of the repository, use your submission to replace the file, commit, and push, but this seems like an very expensive operation with large repositories like the linux kernel.

Any ideas on a more efficient way to add and edit files to a bare repository?


Solution

  • You can use plumbing commands.

    Get your current HEAD, get the tree from there then your blobs.

    Once you have the blob you can put the content in a textbox. When it's finished you just have to hash the new blob, create the new tree, the new commit and tadaam. It's "pushed".

    PS: Remember you're in a bare repository, so check that every command you use don't need the index nor the working directory.


    As it has been asked here is a step by step example.

    First we get the current file content:

    > git cat-file -p HEAD:var/test/text.txt
    test
    

    We do our little modification on that content and now have a new content ready to be pushed. To save that content we're going to hash it:

    > git hash-object -t blob -w var/test/text.txt
    9764d221e6b50063b83c0268544c5d5b745ec9c5
    

    This will save it, and return a sha-1 of that object (blob), the very next step consist in creating a new folder test which will contain our text.txt file. But first let's look at what does the current test folder look like:

    > git ls-tree HEAD:var/test
    100644 blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4    text.txt
    

    So what we want to do here, is replace the previous SHA-1 (9daeafb...) with the new one (9764d22...) and generate a new tree based on that (pay attention to the \t).

    > echo -e "100644 blob 9764d221e6b50063b83c0268544c5d5b745ec9c5\ttext.txt" | git mktree
    b7788f9e8e9a24be31188167a6a0bc1de9e41d24
    

    Great, so now we have the new file text.txt and the parent folder test, we now need var.

    > git ls-tree HEAD:var
    040000 tree 9bfb857f532d280ecd7704beb40a2ea4ba332f5a    test
    
    > echo -e "040000 tree b7788f9e8e9a24be31188167a6a0bc1de9e41d24\ttest" | git mktree
    536f33626a47138499fade7df6d02327f75d80be
    

    and now we need the parent of var (which is the root of our repository):

    > git ls-tree HEAD
    040000 tree 31a6ee5e7d14a0569721632a05234185a109d6bd    var
    
    > echo -e "040000 tree 536f33626a47138499fade7df6d02327f75d80be\tvar" | git mktree
    7db3d6bc14cce98ff89ccc285b9d17965f5ca92b
    

    And it's done, our tree is ready. The only thing missing is the actual commit:

    > git commit-tree -p HEAD -m "commit message" 7db3d6bc14cce98ff89ccc285b9d17965f5ca92b
    4aa2de2cf9e3e4f5470bcd1ee1e83ef6e4025eaf
    

    But it isn't ready yet, now we want the commit to be the HEAD, so the very last step is:

    > git update-ref HEAD 4aa2de2cf9e3e4f5470bcd1ee1e83ef6e4025eaf
    

    And now we're done.


    Resources: