Best Guess Theory
A place to discuss Development techniques, .NET, XNA, NHibernate or anything else that tickles your fancy
Thursday, June 17, 2010
Git for the Lazy
Posting this here for anyone interested. Great, quick, easy Git setup and getting started guide.
Sunday, June 6, 2010
Git Day to Day usage
Part 2 of the Git Guides I wrote up for my XNA team:
Let's discuss how to use Git once you've got your personal repository cloned. First you're going to want to open up Git Bash to follow along. You can use Tortoise and Git Gui to do these processes as well, but the command line has everything I need to be able to walk you through this. So right click on the repo folder (the folder you cloned to) and choose "Open Git Bash Here".
When you clone your repo from your personal repo, Git automatically adds your remote repository as your "origin" repo. You can type in "git remote" and see it listed under "origin". This is your working repository, and what you've got write access to. First things first, you need to add your "upstream" repo. This is typically a read only repository and it's where everyone pulls their changes from. In this case, it's:
- Code:
ssh://git@projects.domain.com/projectMain.git
Type in:
- Code:
git remote add upstream ssh://git@projects.domain.com/projectMain.git
Now when you type in "git remote" you'll have two listings: origin and upstream.
Origin: Your personal repo that you have write access to
Upstream: The main repo that you only have read access to which you fetch changes from
So lets say you've settled in for an evening of coding, here's the typical way you'd begin your night:
- Code:
git fetch upstream
git rebase upstream/master
git branch SomeNewBranchName
git checkout SomeNewBranchName
This 1.) Gets changes from upstream 2.) Rebases your master branch, updating it with the changes from upstream 3.) branches master to a new branch for you to make some changes (favor branching over working on the master branch directly) 4.) switches your current branch to the new branch which changes all the files on your HD so you can start working on your new branch immediately
Say you make a few changes, and now you're ready to commit for the first time. You can run a "git status" to see the files that you've got that have changed. Or you can open up the Git Gui and it'll show you your pending changes. In general, we want to add all the changed files to the repo, like so:
- Code:
git add *
Now your changes are staged you can perform a local commit:
- Code:
git commit -m "Made some changes and these are my checkin notes"
You can continue working on that branch and staging/making local commits with additional notes. That's part of the beauty of a distributed repository source control system, you are your own source control offline. So when you're finally done and you'd like to put what you've got online you can use:
- Code:
git push origin SomeNewBranchName
This will send your local checkin history to the repo you have specified as "origin" (which is the place you cloned from, your writable repo). Now, if that feature is ready to be pulled in to the trunk of our project, you can issue a pull request to the maintainer. Once they receive the request, They'll go in and pull the changes you just pushed to your repo, and then merge them in to the main projects master branch for everyone to see the next time they do a "git fetch upstream".
Now you can run your fetch/rebase process again, and your master branch will be updated with the changes that was pulled in to the trunk.
So, to recap:
1.) Add the main repo as upstream
2.) fetch/rebase to update your local repo
3.) create a new branch whenever you start in on a new feature/change
4.) do lots of small local checkins at logical points as you work on a feature/change so it's easier to roll back to
5.) push to your origin when complete or when you want someone else to check out something you're working on or you want to make sure what you've got gets backed up
Tips:
- Favor branching first over working on master
- Add everyone elses repos to your git config list so you can check out everyone's changes if the need arises. But make sure that you've got your personal repo, and the main upstream repo specified as you'll use those two exclusively during your day to day process
Git Getting started Guide
Wrote a getting started guide for a group of XNA artists/coders that I was on the team of a while back, and got a few requests to turn it into a blog post. This is what I give to people who are new to Git and want to get set up. Next, I'll post a daily usage guide.
Project.Main repository everyone has read access from (so you can set as your upstream to fetch changes from)
Project.: Each persons individual project/repository where you have read/write/commit access to. Everyone has read access to everyone else's repo, except the repo owner and myself (as admin has it by default).
Be sure to have each user clone FROM their specific REPOSITORY, and not from the main project repo.
So outside of GitHub, you handle multiple repositories by actually creating multiple repositories for a project. I like to start by creating a Project.Main and then a Project.UserA, Project.UserB, Project.Me. Everyone has Read/write access to their own repository, and read access to all of the others. I create a repository for myself, as I'm no exception. I don't want to be working directly on the main repo, that should be used for fetches only, and the occasional pushes from me whenever there's something to commit for everyone.
Project.Main repository everyone has read access from (so you can set as your upstream to fetch changes from)
Project.
Be sure to have each user clone FROM their specific REPOSITORY, and not from the main project repo.
Getting started with Git
1.) READ THIS FIRST. This is a great guide to get you started using Git. It's not just an informational read, you need to follow along with the guide if you haven't set it up before.
2.) After using msysgit, you might be a bit more familiar with something like: TortoiseGit, which is a pretty slick UI integration for Git. 99% of what you are going to do with Git can be done through Tortoise or GitGUI, so you can avoid the cmd prompt if you'd like (that seems to scare a lot of people away, myself originally as well). NOTE: During the Tortoise config, choose to use OpenSSH instead of PuTTY (which is a pita to configure).
3.) Take your SSH Key in the .pub file in your .ssh folder in your user dir, and go to "preferences" in the upper right corner of RepositoryHost (you need to log in first of course). Go to the "Public Keys" tab on the right, and "Add Key". Copy/Paste your .pub ssh key (the whole block of text including your email address you used when you registered the SSH key originally) , and hit "add Key", you can give the key name anything. Like "Home PC" or "Laptop" or anything like that.
4.) Clone the repository. Use either GitBash:
5.) Open up your GitBash prompt to your repo directory and type in:
6.) In GitBash, or using gitGui or Tortoise, add the following remote as "upstream": ssh://git@projects.domain/projectMain.git . In GitBash the command would look like:
7.) Congrats, Git's all set up. Now all you do is follow the standard Git workflow. Here's a good article on how to do that(ignore the github specific stuff). Basically it's going to involve you fetching from upstream (The main project: proj.main), Rebasing (updating your local copy to the upstream's new header version, so you don't work on old code), Branch to do some work, commit to your local repo, pushing that to origin, then submitting me a pull request (via PM, Email, etc.). Rinse. Repeat. Check out the official git homepage for more info.
So what does Git give you? If you just went through all that, and you're new to it, you might be asking yourself, wtf, why should I care about this stupid SCM.
I'm glad you asked. SCM (Source Control Management) is very important for projects like these, as it allows us to push up changes that are versioned, just like wikipedia, so if something gets screwed up, or we ever need to go back to a previous implementation/version, we can. SCM allows you to track exactly what changed and why (with ticket linking) so when something breaks, you've got a clear indication of why, and what the intent was behind the change.
Git is a fairly new SCM, and it's a huge buzz, primarily because it's a Distributed Source control. What does that mean? Well, in an SVN model (Subversion) you check in to one centralized server, and when you branch, or push up, it's all managed remotely. If you want to get a previous version, you have to ask the server to get it for you. If you want to work locally on a project an do some branching/investigate versions but you don't have a connection to the server, you're out of luck, because that's where all the info is at.
Git changes that model. Each and every person has their own full image of the Repository on disk. So you can work in an offline model, and more importantly, nearly everything in Git is a branch. Everything you do should be branching for changes, and then pulling those branch changes into your primary branch and merging with master (the trunk). It's also very, very fast, compared to other SCMs, and it has a high degree of flexibility (also it's downside with the learning curve).
There you go, ask away if there's questions or anyone has issues with cloning their repo or getting onto our SCM site.
1.) READ THIS FIRST. This is a great guide to get you started using Git. It's not just an informational read, you need to follow along with the guide if you haven't set it up before.
2.) After using msysgit, you might be a bit more familiar with something like: TortoiseGit, which is a pretty slick UI integration for Git. 99% of what you are going to do with Git can be done through Tortoise or GitGUI, so you can avoid the cmd prompt if you'd like (that seems to scare a lot of people away, myself originally as well). NOTE: During the Tortoise config, choose to use OpenSSH instead of PuTTY (which is a pita to configure).
3.) Take your SSH Key in the .pub file in your .ssh folder in your user dir, and go to "preferences" in the upper right corner of RepositoryHost (you need to log in first of course). Go to the "Public Keys" tab on the right, and "Add Key". Copy/Paste your .pub ssh key (the whole block of text including your email address you used when you registered the SSH key originally) , and hit "add Key", you can give the key name anything. Like "Home PC" or "Laptop" or anything like that.
4.) Clone the repository. Use either GitBash:
- Code:
git clone ssh://git@projects.domain.com/projectname.git
5.) Open up your GitBash prompt to your repo directory and type in:
- Code:
git config core.autocrlf false
6.) In GitBash, or using gitGui or Tortoise, add the following remote as "upstream": ssh://git@projects.domain/projectMain.git . In GitBash the command would look like:
- Code:
git remote add upstream ssh://git@projects.domain.com/projectMain.git
7.) Congrats, Git's all set up. Now all you do is follow the standard Git workflow. Here's a good article on how to do that(ignore the github specific stuff). Basically it's going to involve you fetching from upstream (The main project: proj.main), Rebasing (updating your local copy to the upstream's new header version, so you don't work on old code), Branch to do some work, commit to your local repo, pushing that to origin, then submitting me a pull request (via PM, Email, etc.). Rinse. Repeat. Check out the official git homepage for more info.
So what does Git give you? If you just went through all that, and you're new to it, you might be asking yourself, wtf, why should I care about this stupid SCM.
I'm glad you asked. SCM (Source Control Management) is very important for projects like these, as it allows us to push up changes that are versioned, just like wikipedia, so if something gets screwed up, or we ever need to go back to a previous implementation/version, we can. SCM allows you to track exactly what changed and why (with ticket linking) so when something breaks, you've got a clear indication of why, and what the intent was behind the change.
Git is a fairly new SCM, and it's a huge buzz, primarily because it's a Distributed Source control. What does that mean? Well, in an SVN model (Subversion) you check in to one centralized server, and when you branch, or push up, it's all managed remotely. If you want to get a previous version, you have to ask the server to get it for you. If you want to work locally on a project an do some branching/investigate versions but you don't have a connection to the server, you're out of luck, because that's where all the info is at.
Git changes that model. Each and every person has their own full image of the Repository on disk. So you can work in an offline model, and more importantly, nearly everything in Git is a branch. Everything you do should be branching for changes, and then pulling those branch changes into your primary branch and merging with master (the trunk). It's also very, very fast, compared to other SCMs, and it has a high degree of flexibility (also it's downside with the learning curve).
There you go, ask away if there's questions or anyone has issues with cloning their repo or getting onto our SCM site.
Wednesday, February 17, 2010
Awesome 3D framework for XNA?
Xen 3D XNA Framework
Filing this under things Tuna has recommended me that I don't have time to check into yet, but which look pretty friggan sweet. I'll update later with anything I find on it. Or you guys can tell me if you've had any experience with it ;)
Friday, February 12, 2010
Converting CHM to ePUB (and a e-reader review)
Just got my Astak Ez Reader Pocket Pro 5", and I love it. It's the perfect size, solid resolution, has all the features, all the document support you could ever want, is very fast, has 3 different places to change the page, including link navigation, supports up to a 16gb SD card (aka a metric assload of books), no bullshit DRM and has a battery life measured in months. That's right. Months. This thing just doesn't die. It's brilliant. I've been raving about it for the week or so that I've had it now.
So on to the issue. I have a few technical books as CHM files (Old school, right?), and with the advent of ePub as a standard e-reader format, I wanted a way to convert them into some spiffy epubs so I can take advantage of text reflow (which is also supported in PDFs on the Astak). ePub is an amazing format. Low size, fantastic orientation, and chapter support. Plus, it's a standard document definition, unlike PDF, which is all over the place. It's also quite a good bit faster than PDF to navigate and turn pages on. Faster in the e-reader world typically means less processing, and less processing means longer battery life (plus a happier reader not having to wait as long for the page to turn).
Step1: CHM to HTML.
You have to get the CHM into HTML somehow. There's a few tools to do this. My favorite is ABC Amber's CHM Converter. It does cost, but it's worth it if you've got a lot of CHMs. If you don't, you can still use the trial and get away with not having watermarks crapped out all over the result (I'll explain in a few). You can also use the HH.exe in windows (if that's your platform of choice) to decompile the CHM out into a messy website in a folder of your choice (HH.EXE -decompile C:\Temp\decompile-folder C:\Temp\yourCHM.chm). You can typically convert in one of two ways:
- A single HTML file
- Multiple HTML files (website)
If you go option 1, it's much easier to track and manage, however because of the way ereaders work, navigating pages and chapters will be a huge pain. It'll take quite a few to turn to the next page and 10-15 seconds to navigate chapters on a 600 page book. Option 2 makes turning pages and chapter navigation significantly faster, but involves more work on your end to manage.
We want to go option 2, this is the best speed, and that counts for a lot on an eReader. Now that we've got an output directory containing our "website" we want to investigate the dir. Inside, you should find a single HTML file and a folder of the same name. Open up the folder and go to the images directory (or something similar). Remove any superfluous images such as arrow navigations or whatever else might have been included in the CHM (like header seperators and such). Feel free to also remove any images that you feel might not be of a significant benefit to you reading the book.
Now, look for a main.html or go up a level and look at the html file in the root folder. What you're looking for is the HTML file that acts as the Menu. The menu HTML file is important because it points to all of our chapters/pages for use during the next stage. It's also the place that if you used a trial of Amber CHM, you can do a search and replace for "[Trial Version]" and replace with an empty string and remove all of the "watermarking" that's done during the trial version conversion process. At this point, it's up to you to weed out any chapters you don't want to include in your ePub, such as appendixes, which can help to reduce the "noise" you'll see when navigating the ePub's menu file. Save the file if you've made any changes and continue on to the next step.
Step 2: HTML to ePUB
Going to introduce you to an awesome piece of free OSS software. Calibre. This converts from almost any format into an ePub, and it's great at it. If you're using a single HTML file from step1, then all you need to do here is "Add book" point it at the HTML file, and then "Convert To" and specify ePub as the target source. Done. However that's not what we've chosen to do if you're following along. So when you go to "Add book" in Calibre, you need to select *only* the MENU HTML FILE that you created in the first step. This file is going to tell Calibre where all of the rest of your "chapters" are.
Give Calibre a few moments to import and create a zip containing all of the referenced files. When it's completed, you should see the book appear in the list (hopefully with a size > 0 next to it if you've done it correctly). Right click and go to "Convert". Choose ePub as your output. I'd also recommend deselecting the options for a title page/image. Also select the option in the Menu section for Calibre to generate it's own menu file (it'll clean it up a bit).
Convert. You can now copy the converted ePub to your eReader (which is hopefully an Astak! =p ). Where is it? It's in the Calibre directory created for managing your books. Can't find that? Right click on the book in the Calibre list and go to "View source folder". Viola.
Now you've got a lightning quick ePub file out of a single CHM. Hopefully this helps a few people out :) Good luck!
Friday, February 5, 2010
The NHibernate Dereference pattern
Sometimes, NHibernate associations are so complex that relying on cascades is not always possible or optimal. Other times, you need to ensure that your session deleted object is actually removed from your in memory reference model (e.g. The loaded entities which still reference it) without having to refresh your objects from the database. I ran into this a while back when dealing with a bi-directional ManyToMany in which neither side would allow an all-delete-orphan cascade, and created a variant of the Disposable pattern to resolve it.
I'm sure that other developers implement something similar during their delete process, but I haven't run across anything formally defined. This keeps coming up, and after helping several FluentNHibernate & NHibernate users out by giving them direction I decided to do a writeup. I'm going to try and state the pattern on here for your reference and feedback. Disclaimer: I'm not saying it's perfect, or that there's not something better, I'm just saying it works for what we need, and we haven't found anything better yet.
Intent:
Called before an NHibernate Session.Delete(). Disassociates the entity from the domain model, removing all references to the entity, thereby allowing NHibernate to generate the appropriate UPDATE/DELETE statements in the database, in the order required, so there's no Foreign Key Violations. This pattern works with inheritance.
Implementation:
1: public interface IDereferenceable
2: {3: void Dereference();
4: } 5: 6: public abstract class PersistedBase : IDereferenceable
7: {8: protected bool IsDereferencing;
9: 10: public void Dereference()
11: { 12: Dereference(IsDereferencing); 13: }14: protected virtual void Dereference(bool dereferencing)
15: {16: if (dereferencing)
17: { return; }
18: IsDereferencing = true;
19: 20: //Dereference Children
21: foreach(Child child in Children.ToList())
22: { 23: RemoveChildCore(child); 24: } 25: 26: //Dereference Parents
27: if(parent != null)
28: {29: parent.RemoveChild(this);
30: parent = null;
31: } 32: } 33: 34: private ICollection<Child> children;
35: public IEnumerable<Child> Children{ get { return children; } }
36: public void RemoveChild(Child child)
37: {38: if(!IsDereferencing)
39: { 40: RemoveChildCore(child); 41: } 42: } 43: 44: protected void RemoveChildCore(Child child)
45: { 46: children.Remove(child);47: IDereferenceable deref = child as IDereferenceable;
48: if(deref ! = null)
49: { 50: deref.Dereference(); 51: } 52: } 53: } 54: 55: public class Concrete : PersistedBase
56: {57: protected override void Dereference(bool dereferencing)
58: {59: if (dereferencing)
60: { return; }
61: IsDereferencing = true;
62: 63: //Dereference Children
64: foreach(StepChild stepChild in StepChildren.ToList())
65: { 66: RemoveStepChildCore(stepChild); 67: } 68: 69: //Dereference Parents
70: if(stepParent != null)
71: { 72: stepParent.RemoveChild(this);
73: stepParent = null;
74: } 75: 76: base.Dereference(false);
77: } 78: 79: private ICollection<StepChild> stepChildren;
80: public IEnumerable<StepChild> StepChildren{ get { return stepChildren; } }
81: public void RemoveStepChild(StepChild stepChild)
82: {83: if(!IsDereferencing)
84: { 85: RemoveStepChildCore(stepChild); 86: } 87: } 88: 89: protected void RemoveStepChildCore(StepChild stepChild)
90: { 91: stepChildren.Remove(stepChild);92: IDereferenceable deref = stepChild as IDereferenceable;
93: if(deref ! = null)
94: { 95: deref.Dereference(); 96: } 97: } 98: }Let's go down the list:
- Our abstract class/interface. This is the base class that handles basic dereference association and implements the IDereferenceable interface.
- IsDereferencing - This is similar to the IsDisposing/Disposed flag on IDisposable objects. It's a way to ensure that we only Dereference an object once. This is only set within the protected Dereference method if it's passed in a !dereferencing parameter.
- Dereference() - Public no-parameter. This is what is called by by the code that's about to delete the object. It's also called by other classes whenever they want to dereference this instance as a child reference. Passing in the current IsDereferencing flag prevents us from getting into a cyclic call if it's called several times by many parent classes.
- Dereference(bool dereferencing) A.) We early escape if our dereferencing parameter is true, so we don't perform any dereference logic more than once B.) Dereference children first. Children are typically going to be defined as collections, but there are always exceptions. C.) We enumerate over a copy of the collection. Reason? We're going to get a collection cannot be modified during enumeration exception otherwise. D.) We call the private RemoveChildCore(child), which does not do a dereferencing check (otherwise nothing would happen) E.) We check first to see if Parent is null, this is primarily here for situations where a Parent is allowed to be null, or during testing where you may not popular all the parents of an entity F.) If parent is not null, then we call the public RemoveChild(this) method on the parent so we're no longer referenced in a parent collection. Important to note, that RemoveChild() on the parent does perform a !IsDereferencing check on the parent, just like we have for the child. After all, the Child's Dereference() could have been called by the Parent's Dereference(), so we want to respect this. G.) We null out the parent
- Our collection is exposed out as a read only collection, which tells the user that we do not allow adding/removing directly on the collection. Good practice and safety precaution if you have additional association logic, which you would put into the AddChild(Child child) method (not implemented here for the sake of simplifying the example).
- The public RemoveChild(Child child) checks if we're dereferencing, if we are, then we skip actually removing the child. This is there so that when children are being dereferenced, and call parent.RemoveChild(this), they do not enter a cyclic loop. If not dereferencing, call Core dereference.
- RemoveChildCore(Child child) has no checks, and is private, so it should be tightly controlled by the class. This will remove the item from the private collection, cast the child as a dereferenceable, and then if the child is dereferenceable, it will proceed to call Dereference on it. NOTE: that child.Dereference() should only be called if the child dies with the parent, so if the parent is dereferenced, the child should be as well. in some cases, this is not the case, and there might only be an incidental association between them, such as is the case with a ManyToMany() association. In this case, dereferencing the child should be avoided (perhaps though you need to keep the two lists synchronized in the MTM example, so you'd call child.RemoveParents(this) in place of the child.Dereference() )
- The Concrete implementation of the base class. Here we have a similar setup as the base class, except we've got additional information that needs to be dereferenced (another collection and parent, defined as StepChildren and StepParent)
- Dereference(bool dereferencing). Our setup here is the same, and can be referenced from 4 A-G. However, it adds a new element at the end: H.) We call our base.Dereference(false) to continue up the dereference stack. Passing it false is important, since we know we're in the middle of dereferencing and should override the dereferencing check. If we passed the IsDereferencing field, the base Dereference would not fire, meaning that any references specific to the base class would still hold a reference to our class. As we're overriding, polymorphism dictates that our bottom level class (most concrete) will have Dereference called on it first. So it Dereferences from most specific to most generic (Covarience).
Usage:
1: using(var session = sessionFactory.OpenSession())
2: using(var transaction = session.BeginTransaction())
3: { 4: var concrete = session.Get(id); 5: concrete.Dereference(); 6: session.Delete(concrete); 7: transaction.CommittTransaction(); 8: }So there you go, a way to manage the disassociations with NHibernate (or another ORM) and your object model. Hopefully this helps someone out, and if it doesn't, feel free to leave a comment if you've got any critiques or questions. Always open to feedback! (Also wrote all of the code in notepad, so if anything's off let me know and I'll correct it)
Labels:
Best Practice,
C#,
Development,
NHibernate,
SQL
Friday, January 15, 2010
CodeMash 2010 Session Materials
Just finished my second session at CodeMash. Was a good time, and had a pretty packed house. As promised:
Here's my session materials
Thanks for attending, and CodeMash has been pretty amazing. Will come to Ohio again ;)
Subscribe to:
Posts (Atom)