Skip to content

Instantly share code, notes, and snippets.

@nulltoken
Created April 12, 2012 22:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nulltoken/2371499 to your computer and use it in GitHub Desktop.
Save nulltoken/2371499 to your computer and use it in GitHub Desktop.
[LibGit2Sharp] *Untested* mocked proof of concept of a TreeDefinition - A building block to ease the generation of Trees in the odb
using System;
using System.Collections.Generic;
namespace ConsoleApplication97854
{
class Program
{
static void Main(string[] args)
{
/*
Building a Tree with the following structure
"/path/to/another/file3"
"/path/to/file1"
"/path/to/file2"
*/
var repo = new Repository();
/*
* Nested version
*/
var rootInline =
new TreeDefinition()
.Add("path", new TreeDefinition()
.Add("to", new TreeDefinition()
.Add("another", new TreeDefinition()
.Add("file3", "/path/another/file3"))
.Add("file2", "/path/to/file2")
.Add("file1", "/path/to/file1")));
Tree inlineTree = repo.ObjectDatabase.Insert(rootInline);
/*
* Flatened version
*/
var another = new TreeDefinition()
.Add("file3", "/path/another/file3");
var to = new TreeDefinition()
.Add("another", another)
.Add("file2", "/path/to/file2")
.Add("file1", "/path/to/file1");
var path = new TreeDefinition()
.Add("to", to);
var root = new TreeDefinition().Add("path", path);
Tree tree = repo.ObjectDatabase.Insert(root);
/*
* Non hierarchical version
*/
var flatIsBack = new TreeDefinition2()
.Add("path/to/another/file3", "/path/to/another/file3")
.Add("path/to/file1", "/path/to/file1")
.Add("path/to/file2", "/path/to/file2");
Tree otherTree = repo.ObjectDatabase.Insert(flatIsBack);
/*
A Tree with the following structure already has just been built in the repo
"/path/to/another/file3"
"/path/to/file1"
"/path/to/file2"
A new entry Blob "file4" is added under "/path/to".
We want to create a new Tree containing the previous entries and the new one
*/
/* Easy one, but not that realistic: update the flatened TreeDefinition */
to.Add("file4", "/path/to/file4");
Tree newTree = repo.ObjectDatabase.Insert(root);
/* A bit less cheesy: Build a new TreeDefinition reusing existing Trees and Blobs */
var anotherTreeEntry = tree["path/to/another"];
var file1TreeEntry = tree["path/to/file1"];
var file2TreeEntry = tree["path/to/file2"];
var update =
new TreeDefinition()
.Add("path", new TreeDefinition()
.Add("to", new TreeDefinition()
.Add(anotherTreeEntry)
.Add(file2TreeEntry)
.Add(file1TreeEntry)
.Add("file4", "/path/to/file4")));
Tree updatedTree = repo.ObjectDatabase.Insert(update);
/* Another option: let's get rid of the hierarchical approach */
var update2 = new TreeDefinition2()
.Add(anotherTreeEntry)
.Add(file2TreeEntry)
.Add(file1TreeEntry)
.Add("path/to/file4", "/path/to/file4");
/* Idea:
- Find a way to generate a TreeDefinition from a Tree (Not that complex, but could take time on a large DAG, unless it's lazily built)
- Signature proposal: TreeDefinition TreeDefinition.From(Tree tree);
- Allow Removal and Update of entries
- Submodules and Symlinks?
*/
// Unfortunately this is...
throw new NotImplementedException();
}
}
class ObjectDatabase
{
internal readonly Repository _repo;
public ObjectDatabase(Repository repo)
{
_repo = repo;
}
public bool Contains(string oid)
{
return true; //Dummy value
}
public Blob Insert(string path)
{
return null; //Dummy action
}
public Tree Insert(TreeDefinition td)
{
return td.Build(this);
}
public Tree Insert(TreeDefinition2 td)
{
return td.Build(this);
}
}
internal class Repository
{
public Repository()
{
ObjectDatabase = new ObjectDatabase(this);
}
public ObjectDatabase ObjectDatabase { get; private set; }
public T Lookup<T>(string sha) where T : new()
{
return new T();
}
}
internal class TreeDefinition
{
private readonly IList<Func<ObjectDatabase, Tuple<string, string, GitObject>>> booh = new List<Func<ObjectDatabase, Tuple<string, string, GitObject>>>();
/// <param name="name">Name of the Blob in the Tree to be created</param>
/// <param name="fullpath">Full path to a file which content will be stored in a Blob</param>
public TreeDefinition Add(string name, string fullpath)
{
booh.Add((odb) =>
{
var mode = RetrieveAttributes(fullpath);
var entry = odb.Insert(fullpath);
return new Tuple<string, string, GitObject>(name, mode, entry);
});
return this;
}
private string RetrieveAttributes(string fullpath)
{
return "100644"; //Dummy value
}
public TreeDefinition Add(string name, Blob entry, string mode)
{
booh.Add((odb) => new Tuple<string, string, GitObject>(name, mode, entry));
return this;
}
public TreeDefinition Add(string name, Tree entry)
{
booh.Add((odb) => new Tuple<string, string, GitObject>(name, "040000", entry));
return this;
}
public TreeDefinition Add(TreeEntry entry)
{
booh.Add((odb) => new Tuple<string, string, GitObject>(entry.Name, entry.Attributes, entry.Target));
return this;
}
public TreeDefinition Add(string name, TreeDefinition definition)
{
booh.Add((odb) =>
{
var entry = definition.Build(odb);
return new Tuple<string, string, GitObject>(name, "040000", entry);
});
return this;
}
internal Tree Build(ObjectDatabase odb)
{
NativeMethods.git_treebuilder_create();
foreach (var func in booh)
{
Tuple<string, string, GitObject> tp = func.Invoke(odb);
NativeMethods.git_treebuilder_insert(tp.Item1, tp.Item3.Oid, tp.Item2);
}
string tree_oid;
NativeMethods.git_treebuilder_write(out tree_oid);
NativeMethods.git_treebuilder_free();
return odb._repo.Lookup<Tree>(tree_oid);
}
}
internal class TreeDefinition2
{
private readonly IList<Func<ObjectDatabase, Tuple<string, string, GitObject>>> booh = new List<Func<ObjectDatabase, Tuple<string, string, GitObject>>>();
private Dictionary<string, TreeDefinition2> definitions = new Dictionary<string, TreeDefinition2>();
/// <param name="targetPath">Path to the Blob within the Tree to be created</param>
/// <param name="fullpathOnDisk">Full path to a file which content will be stored in a Blob</param>
public TreeDefinition2 Add(string targetPath, string fullpathOnDisk)
{
ProcessMultiSegmentsPath(targetPath,
(def, subPath) => def.Add(subPath, fullpathOnDisk),
name => booh.Add(odb =>
{
var mode = RetrieveAttributes(fullpathOnDisk);
var entry = odb.Insert(fullpathOnDisk);
return new Tuple<string, string, GitObject>(name, mode, entry);
})
);
return this;
}
private void ProcessMultiSegmentsPath(string path, Action<TreeDefinition2, string> subPathAdder, Action<string> nameAdder)
{
var segments = SplitPath(path);
// We're dealing with a name
if (segments.Item2 == null)
{
nameAdder(segments.Item1);
return;
}
// We're dealing with a real path
TreeDefinition2 def = AddOrRetrieveTreeDef(segments.Item1);
subPathAdder(def, segments.Item2);
}
private TreeDefinition2 AddOrRetrieveTreeDef(string treeName)
{
TreeDefinition2 def;
if (definitions.TryGetValue(treeName, out def))
return def;
def = new TreeDefinition2();
definitions.Add(treeName, def);
return def;
}
private Tuple<string, string> SplitPath(string targetPath)
{
var segments = targetPath.Split(new [] { '/' }, 2);
return new Tuple<string, string>(segments[0], segments.Length == 2 ? segments[1]: null);
}
private string RetrieveAttributes(string fullpath)
{
return "100644"; //Dummy value
}
public TreeDefinition2 Add(string targetPath, Blob entry, string mode)
{
ProcessMultiSegmentsPath(targetPath,
(def, subPath) => def.Add(subPath, entry, mode),
name => booh.Add((odb) => new Tuple<string, string, GitObject>(targetPath, mode, entry))
);
return this;
}
public TreeDefinition2 Add(string targetPath, Tree entry)
{
ProcessMultiSegmentsPath(targetPath,
(def, subPath) => def.Add(subPath, entry),
name => booh.Add(odb => new Tuple<string, string, GitObject>(name, "040000", entry))
);
return this;
}
public TreeDefinition2 Add(TreeEntry entry)
{
ProcessMultiSegmentsPath(entry.Path,
(def, subPath) =>
{
if (entry.Target is Blob)
def.Add(subPath, (Blob)(entry.Target), entry.Attributes);
else
def.Add(subPath, (Tree)(entry.Target));
},
name => booh.Add((odb) => new Tuple<string, string, GitObject>(entry.Name, entry.Attributes, entry.Target))
);
return this;
}
internal Tree Build(ObjectDatabase odb)
{
NativeMethods.git_treebuilder_create();
foreach (var func in booh)
{
Tuple<string, string, GitObject> tp = func.Invoke(odb);
NativeMethods.git_treebuilder_insert(tp.Item1, tp.Item3.Oid, tp.Item2);
}
string tree_oid;
NativeMethods.git_treebuilder_write(out tree_oid);
NativeMethods.git_treebuilder_free();
return odb._repo.Lookup<Tree>(tree_oid);
}
}
internal class Blob : GitObject
{
public string mode;
}
internal class Tree : GitObject
{
public TreeEntry this[string path]
{
get { return new TreeEntry(); }
}
}
internal class TreeEntry
{
public string Attributes;
public string Name;
public string Path;
public GitObject Target { get { return null; } }
}
internal class GitObject
{
public string Oid;
}
internal class NativeMethods
{
public static int git_treebuilder_create()
{
return 0;
}
public static int git_treebuilder_insert(string filename, string id, string mode)
{
return 0;
}
public static int git_treebuilder_write(out string sha)
{
sha = "toto";
return 0;
}
public static int git_treebuilder_free()
{
return 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment