After a while cross-posting, I decided to retire this blog in favor of my blog on Los Techies. Hope you’ll join the conversation there!
After a while cross-posting, I decided to retire this blog in favor of my blog on Los Techies. Hope you’ll join the conversation there!
On a whim, I’ve decided to tag a v0.3 release of posh-git (which has been stable for a few months now). In this release…
Previously the setup process for posh-git was undefined. Daniel Hoelbling was kind enough to put together a getting-started post, but I decided to make it even easier. Assuming Git and PowerShell are configured correctly (see readme for details), getting started is trivial:
cd C:\Wherever git clone https://github.com/dahlbyk/posh-git.git .\posh-git\install.ps1
At this point the sample posh-git profile will be loaded as part of your PowerShell profile. If you don’t like the sample profile, feel free to grab the pieces you want and discard the rest (so you can use posh-hg too, perhaps).
Update: If you already have posh-git installed, just cd
into your posh-git directory and pull from my master
branch:
# If you don't already have me as a remote... git remote add dahlbyk git pull --rebase dahlbyk master
You don’t need to run install.ps1
again; just open a new PowerShell session and you’re good to go.
By taking a dependency on msysgit 1.7.1, all status information is now retrieved in a single call (git status -s -b
). This still means git status
is called for every prompt, so if status
is slow for your repository your prompt will be slow too.
If it’s still too slow for your taste, you also have the option to set $GitPromptSettings.EnableFileStatus = $false
. This will preserve branch information for the prompt, but skip everything else (counts and tab completion for added/modified/deleted files).
Finally, you can set $GitPromptSettings.Debug = $true
to see how long the various steps take behind the scenes. If your environment is anything like mine, the majority of the time will be spent in git
calls.
git rm
during deleted/updated merge conflictcherry-pick
, diff
, difftool
, log
and show
alias.dt = difftool
supports git dt <tab>
)As always, your feedback is appreciated. If you’d like posh-git updates between release posts, you can also follow me on Twitter.
One of the new features of ASP.NET MVC 3 is a controller-level attribute to control the availability of session state. In the RC the attribute, which lives in the System.Web.SessionState
namespace, is [ControllerSessionState]
; for RTM ScottGu says it will be renamed simply [SessionState]
. The attribute accepts a SessionStateBehavior
argument, one of Default
, Disabled
, ReadOnly
or Required
. A question that came up during a Twitter discussion a few weeks back is how the different behaviors affect Html.RenderAction()
, so I decided to find out.
I started with an empty MVC 3 project and the Razor view engine. We’ll let a view model figure out what’s going on with our controller’s Session
:
public class SessionModel { public SessionModel(Controller controller, bool delaySession = false) { SessionID = delaySession ? "delayed" : GetSessionId(controller.Session); Controller = controller.GetType().Name; } public string SessionID { get; private set; } public string Controller { get; private set; } private static string GetSessionId(HttpSessionStateBase session) { try { return session == null ? "null" : session.SessionID; } catch (Exception ex) { return "Error: " + ex.Message; } } }
The model is rendered by two shared views. Index.cshtml
gives us some simple navigation and renders actions from our various test controllers:
@model SessionStateTest.Models.SessionModel @{ View.Title = Model.Controller; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Host: @Model.Controller (@Model.SessionID)</h2> <ul> <li>@Html.ActionLink("No Attribute", "Index", "Home")</li> <li>@Html.ActionLink("Exception", "Index", "Exception")</li> <li>@Html.ActionLink("Default", "Index", "DefaultSession")</li> <li>@Html.ActionLink("Disabled", "Index", "DisabledSession")</li> <li>@Html.ActionLink("ReadOnly", "Index", "ReadOnlySession")</li> <li>@Html.ActionLink("Required", "Index", "RequiredSession")</li> </ul> @{ Html.RenderAction("Partial", "Home"); Html.RenderAction("Partial", "Exception"); Html.RenderAction("Partial", "DefaultSession"); Html.RenderAction("Partial", "DisabledSession"); Html.RenderAction("Partial", "ReadOnlySession"); Html.RenderAction("Partial", "RequiredSession"); }
Partial.cshtml
just dumps the model:
@model SessionStateTest.Models.SessionModel <div>Partial: @Model.Controller (@Model.SessionID)</div>
Finally, we need a few test controllers which will all inherit from a simple HomeController
:
public class HomeController : Controller { public virtual ActionResult Index() { return View(new SessionModel(this)); } public ActionResult Partial() { return View(new SessionModel(this)); } } [ControllerSessionState(SessionStateBehavior.Default)] public class DefaultSessionController : HomeController { } [ControllerSessionState(SessionStateBehavior.Disabled)] public class DisabledSessionController : HomeController { } [ControllerSessionState(SessionStateBehavior.ReadOnly)] public class ReadOnlySessionController : HomeController { } [ControllerSessionState(SessionStateBehavior.Required)] public class RequiredSessionController : HomeController { }
And finally, a controller that uses the SessionModel
constructor’s optional delaySession
parameter. This parameter allows us to test RenderAction
‘s Session
behavior if the host controller doesn’t use Session
:
public class ExceptionController : HomeController { public override ActionResult Index() { return View(new SessionModel(this, true)); } }
So what do we find? Well the short answer is that the host controller’s SessionStateBehavior
takes precedence. In the case of Home
, Default
, ReadOnly
, and Required
, we have access to Session
information in all rendered actions:
If the host controller is marked with SessionStateBehavior.Disabled
, all the rendered actions see Session
as null
:
I see this is the key finding to remember: an action that depends on Session
, even if its controller is marked with SessionStateBehavior.Required
, will be in for a nasty NullRef surprise if it’s rendered by controller without. It would be nice if the framework either gave some sort of warning about this, or if they used a Null Object pattern instead of just letting Session
return null
.
Finally, things get really weird if a Session
-dependent action is rendered from a host controller that doesn’t reference Session
, even if SessionState
is enabled:
It’s pretty clear the issue has something to do with where RenderAction()
happens in the request lifecycle, but it’s unclear how to resolve it short of accessing Session
in the host controller.
So there we have it…a comprehensive testing of sessionless controllers and RenderAction
for the ASP.NET MVC 3 Release Candidate. Hopefully the inconsistencies of the latter two cases will be resolved or at least documented before RTM.
In the spirit of “simplest thing that works,” my team has a rather low-fidelity approach to code reviews: patch files and e-mail. Nothing fancy, but we find it works rather well. It’s even easier thanks to git format-patch
, which lets me easily generate a patch per commit, but I was never able to get send-email
to work quite like I wanted. Instead, I whipped together a PowerShell script in a few minutes that does just the trick:
function patch ($ref = 'master..', $Message = '', [switch]$KeepFiles) { $patchPaths = $(git format-patch -C -o C:/Temp/Patches $ref) if($patchPaths) { $outlook = New-Object -ComObject Outlook.Application $mail = $outlook.CreateItem(0) [void]$mail.Recipients.Add('myteam@mycompany.com') $mail.Subject = "Review - $Message" $commits = $(git log -C --pretty=format:'%s' --reverse $ref) | foreach { "<li>$_</li>" } $mail.HTMLBody = "<ol style=`"font: 11pt Calibri`">$commits</ol>" $patchPaths | foreach { [void]$mail.Attachments.Add($_) if(!$KeepFiles) { Remove-Item $_ } } $mail.Display() } else { Write-Warning 'Nothing to patch!' } }
Create patch of everything on current branch since master:
patch -m "Issue 123 - This is neat"
Create patch of last two commits, without message:
patch HEAD~2..
Create patch of everything except the current commit, with message:
patch master..HEAD~1 'Refactoring for Story 234'
I’ve been using git-svn for almost a year now, and have settled on a low-friction workflow that has been working really well. First, a few notes about how I work with Git and Subversion…
push
and pull
for collaborating with others. git-svn stores extra metadata that isn’t pushed with the rest of the repository, so git-svn operations will fail on cloned repos. Furthermore, each repository’s git-svn commits are unique, so pulling from another repository will fetch its parallel history (if you have to do this, git rebase -i
can help filter out the overlapping commits). That said, I do use push/pull to collaborate with myself across machines, though I always operate against Subversion from one machine.First up is the alias that stands in for svn update
, to make sure I’m always working from the latest changes:
!git svn fetch && git push . remotes/trunk:master && git push -f origin master:master && git rebase master
Update Nov. 30, 2010: After reports that people were having issues calling git up
from master, I’ve modified the alias slightly:
!git svn fetch && git svn rebase -l && git push . remotes/trunk:master && git push -f origin master
If this is your first alias, the command to set the alias would be:
git config alias.up "!git svn fetch && git svn rebase -l && git push . remotes/trunk:master && git push -f origin master"
Step by step, this alias does the following:
svn fetch
instead of svn rebase
because the former also fetches from Subversion branches; we’ll do our own rebase later.-l
skips a remote fetch, since we just did one.--std-layout
, you might need to replace remotes/trunk with remotes/git-svn or whatever your git-svn ref is.The key is item #2: automating the synchronization of master and Subversion means I never have to think about it again. I can either rebase a topic branch against master to get reasonably fresh commits, or use git up
to grab the latest (mostly when preparing to commit into Subversion). Without this alias, I tended to waste a fair amount of time switching back to master periodically just to make sure it’s up-to-date enough.
You can use git up
pretty much any time you want — on master, on a topic branch or even with a detached HEAD — as long as your working copy and index are clean. If you have work in progress but need Subversion’s latest, you can either git stash
or make a temporary commit and then git reset HEAD^
after the update. I used to favor the former, but am starting to prefer the latter because I tend to be undisciplined about cleaning up stashes that git stash pop
didn’t delete due to merge conflicts.
Satisfied with this abstraction for pulling changes from Subversion, I then applied the same logic to committing into Subversion:
!git svn dcommit && git push . remotes/trunk:master && git push -f origin master && git checkout master
The dci alias (short for dcommit) does the following:
git up
, resolve conflicts and try again.git rebase master branch-name
) and get back to work.Again, by automating most of what I was already doing I can be confident that I will always come out of a dcommit in a well-known, consistent state from which I can proceed without extra thought.
git dci
should be used when you want to commit HEAD and its uncommitted ancestors into Subversion. In my experience, this usually falls into one of three scenarios:
You have a topic branch and want to replay all of its commits into Subversion one by one. In this case, simply checkout the branch and call git dci
:
I use this most often, as I prefer granular commits and a linear history.
Update Dec. 4, 2010: Donn pointed out that I gloss over my use of an lg
alias, which provides a concise graph of history. The alias is described here.
The exception to my linear history preference is if the build would break between intermediate commits. For example, I might upgrade a dependency in one commit and then fix the build in the next commit. One could certainly use a squash merge or interactive rebase, but sometimes I prefer to keep the granular history in Git.
So how do we accomplish this with git-svn? Well git-svn essentially treats merge commits as the sum of their parts, relative to the previous Subversion commit — it squashes for us. To make our single Subversion commit, we’ll just switch to master and use git merge --no-ff
:
The --no-ff
flag forces the creation of a merge commit even though we should be able to fast-forward (if we up’d first, that is). You can make this the default behavior for master by setting branch.master.mergeoptions (I use --no-ff --no-commit
).
Once we have our merge commit, we again use git dci
to push all its changes into Subversion:
Note that the dev2 commits remain untouched by git-svn, and Subversion has the combined changes:
The final scenario is really no different from either #1 or #2, but it’s worth pointing out that you can use git dci
from any HEAD, not just on a branch. For example, suppose I have a few refactoring commits that I created as part of feature work which I would like to share with the team now while I finish up the feature. In this case, I can checkout (or merge from) the last of the commits I want to share:
And git dci
from the detached HEAD to save those changes:
Note that git dci
left me on master. Now to continue on dev3, I just rebase against master so Git is aware that the dci’d commits have been updated with git-svn metadata:
If I want to get even more sophisticated, I could cherry-pick then dci individual commits, or I could create a copy of the branch and use interactive rebase to exclude the commits that I don’t want to dci yet. Just remember to rebase the topic branch against master when you’re done.
If you have any additional git-svn tips or questions, please let me know.
Reading through Jason’s post on using Git-Achievements with msysGit, I couldn’t help but get it working with PowerShell. The result is a single PowerShell script added to my Git-Achievements repository, tagged here on the off chance I decided to upload my achievements.
To install posh-git-achievements…
Set-Alias git C:\Git\git-achievements\git-achievements.ps1
. $PROFILE
git achievements --help
If all goes according to plan, this should unlock your first achievement.
Note that this will pass every git
call through a few extra layers, including calls made for the posh-git prompt. But if you can tolerate the performance hit, it’s a rather fun way to expand your working knowledge of Git. Enjoy!
I had never had any problems using msysGit with SSL until last night, when I came across the following error:
$ git pull Password: error: error setting certificate verify locations: CAfile: /bin/curl-ca-bundle.crt CApath: none while accessing https://dahlbyk@github.com/dahlbyk/posh-git.git/info/refs fatal: HTTP request failed
There were a number of suggestions in the comments on GitHub’s Smart HTTP post, but they mostly seemed like hacks (most common: copy file from msysGit elsewhere, or turn off http.sslverify
). A much easier fix is just to set http.sslcainfo
to the absolute path of the curl-ca-bundle.crt
file in your msysGit install’s bin
folder:
$ git config --global http.sslcainfo "/c/Program Files (x86)/Git/bin/curl-ca-bundle.crt"
I chose to do this at the --global
level so the setting won’t be overwritten by future msysGit installs.
I just tagged the v0.2 release of posh-git, which you can download here. This is the last release supporing msysgit 1.6.5 and 1.7.0. In this release…
Thanks to a contribution from David Muhundro, posh-git now exposes its functions through a module (.psm1). The module exposes a number of functions whose usage can be seen in the example profile.
tgit <tab>
git svn <tab>
git stash
operations: show, apply, drop, pop, branch
git reset
and git rebase
git rm
git rebase -i <tab>
works as expected.Thanks to Jeremy Skinner and Mark Embling for their contributions to this release.
The most common complaint about posh-git is performance, which has already been addressed for the next release (available in my master branch). However, the fix requires taking a dependency on msysgit 1.7.1, which has not been officially released yet. Still, it has been working fine for me.
Beyond that, we still need to address the first two items on my list from the last release…
If you have any other feature requests or find issues, please let us know.
One of my favorite developer events of 2009 was St. Louis Day of .NET. Not only were the facilities (Ameristar Casino) top-notch, but there were a ton of great sessions and I got to pick the brains of some really sharp people. This year’s event looks to be even better, with a huge variety of sessions on principles, practices and plenty of programming. I will be presenting two sessions:
Dynamic .NET has gone mainstream with the recent promotion of the Dynamic Language Runtime into .NET 4. This session will discuss what the DLR is, how it works with C# 4 and Visual Basic 10, and why this doesn’t mean C# has jumped the shark. We will also look at some ways in which these features can be used to solve real-world problems.
System.Interactive is a library distributed with Microsoft’s Reactive Extensions, currently available on DevLabs, which provides a number of useful extensions to the LINQ Standard Query Operators. These extensions include operators to add and contain side effects, handle exceptions, generate and combine sequences, and much more. This session will review the new operators and discuss interesting problems they can be used to solve. Note that Rx is available for .NET 3.5 SP1, Silverlight 3 and .NET 4.0, so this session is not just for those developing on the bleeding edge.
The organizers were kind enough to provide speakers with some discount codes, so I figured this is as good a place as any to give those out. Two lucky commenters will get a code worth $75 off the cover price, with the grand prize being free admission. All you have to do is leave a comment (with a valid e-mail address) convincing me that you deserve these rich rewards over my other suitors. And if your reasons are all terrible, I’ll ask random.org. Deadline is 23:59 CDT on Monday, July 26th.
Hope to see you there!
I’m not going to bother with a review of Martin Fowler‘s Refactoring: Improving the Design of Existing Code. It’s good enough that its catalog, available in expanded form online, now provides the definitive vocabulary shared by dozens of refactoring tools across nearly every major development platform. Though I was already familiar with the majority of the catalog, I thought it would be worth reading the other chapters, the notes from which you will find below with my additions indicated with emphasis. I’ve also included thoughts on various entries in the catalog.
An expanded catalog of code smells is available online.
If this is the first you’ve read of unit testing, check out a book dedicated to the subject like Pragmatic Unit Testing.
These are hard to identify, but provide the biggest return on investment. Think of them as high-level goals accomplished through low-level changes from the above catalog.