In Source Control Explorer, you're always operating on pending space. At the command line, it's less clear. Consider the following setup: you're working in a directory $/project that's mapped to c:\project and contains two files a.cs and b.cs. For the sake of argument, we'll say they have itemIDs 1 and 2.
c:\project>tf ren b.cs c.csc:\project>tf ren a.cs b.csc:\project>tf edit $/project/b.cs
Which item gets the edit pended on it? I actually wouldn't know without trying it, because our syntax in TFS v1 is rather haphazard: different tf commands have different rules. Regardless, we can use our new namespace terminology to frame the possible answers. If 'tf edit <serverpath>' operates on pending space, then the answer is item #1. If it operates on committed space, then #2 gets checked out. Ambiguous.
After v1 we tried to clean up this behavior. The new guiding rule is that local paths passed on the command line will be interpreted as pending space, while server paths will be treated as committed space. We feel this is the best compromise between user experience and predictability.
Users usually expect to operate on names as they see them appear on disk. That's pending space: added/branched/undeleted files are present, deleted files are gone, and renames are in effect. You can copy/paste paths from Explorer and it will just work. However, you can't do everything in pending space that you can in committed space. You can't use a pending item as the source of a Branch or Merge; you can't combine a Delete with most other pending changes; and so on. In v1 we allowed such considerations to inspire a plethora of rules like "branch always operates on committed space" that nobody can remember. We were also very forgiving -- for instance, we might allow 'tf <change> $/project/c.cs' or 'tf <change> a.cs' to succeed so long as there was no ambiguity.
Going forward we think it's better to throw an error every now & then if it keeps things more consistent overall. A full review of our APIs with this principle in mind probably won't happen before Rosario, though. Feedback is welcome on this topic!
But wait, there's another wrinkle yet.
Which item gets the edit pended on it? Recall that until you run Get, the new mapping does not take effect on disk. That is, a.cs and b.cs are still in their original locations. Yet $/project/b.cs unquestionably maps to a.cs according to our rules, and the server knows this. If we wanted to be pedantic, we'd define another namespace called "unsynchronized local space" to capture the difference between the two viewpoints.
In the end, it comes down to implementation details. When you call Edit, the server looks at the direct item <-> local space mapping that's cached in the LocalVersion table. This is certainly the fastest way to resolve the mapping, and it also happens to coincide with the reality on disk: c:\project\a.cs has item #1's contents at the time you made the call, and item #1 is in fact checked out.
'tf undo' is a special case: we have to perform a Get as part of the operation anyway, so we let you specify either local path.
Clearly, if you combine this quirk with pending renames (or worse, pending parent renames) you could get your brain in quite a twist trying to figure out the correct syntax. In all cases, running Get will clean everything up according to the new mappings.
Our design here is "case-sensitive storage, case-insensitive lookup." Put another way, outputs are case-sensitive while inputs are case-insensitive. To meet that goal, we need these rules:
*I think this is the first time I've used the phrase "Rename bit". Internally, we represent the various changetypes as bitflags that can be combined in a surprising # of ways. I'll post the complete chart sometime. Suffice to say that for our purposes, operations like 'tf undelete /newname', or a Merge that brings renames into the target, count because they set the Rename bit.