"svn update" will randomly delete your work with no way to ever get it back.
I don't think new git users migrating from svn actually understand that they can rollback any changes git makes. If a pull goes bad and you don't want to deal with it, just reset to your last head. All is forgotten until you feel like fixing it. And no data is ever lost.
(svn loses data by merging everything it can with your uncommitted changes, and then barfing when it can't do the merge. You're left with a bunch of conflicted file, and a bunch of merged files, with no way to ever get the unmerged files again. With git, this simply cannot happen. Git will not touch untracked files or unstaged changes, it will just die. And what it does automatically merge can be unmerged with reset. Or, you can commit the conflict markers to a separate branch, deal with it later, undo that commit, and move on with your life. Git makes easy things a little harder, but hard things easy. Subversion makes anything except hair loss very difficult.)
Oh, and also, the OP really wants his tree to end up like:
A--B--D--C
This can be achieved with "git pull --rebase" (or automagically with the config key branch.<name>.rebase = true). That is what Subversion does, modulo the ability to revert to your original branch.
"svn update" will randomly delete your work with no way to ever get it back.
No, it will not.
You're left with a bunch of conflicted file, and a bunch of merged files, with no way to ever get the unmerged files again.
Umm... no? Take a look at conflicted-filename.mine. Whenever there is a conflict, svn appends .mine to your copy of the file. After that, it creates conflicted-filename.r123 and conflicted-filename.r124, and goes crazy with the angle brackets in conflicted-filename.
I've lost plenty of data with git. Most of it has to do with innocuous-sounding commands that don't ask for confirmation when deleting data. For example, git checkout filename is equivalent to svn revert filename. Of course git checkout branchname does something completely different. If a branch and a file share the same name, git will default to switching branches, but that doesn't stop bash autocomplete from ruining the day.
Here's a crazy idea: If you have an innocuous action and a dangerous action, do not label them with the same command.
Yes it will. Imagine you check out revision 1, which consists of two files:
foo:
a
bar:
b
You do some hacking, and end up with:
foo:
a
bar:
b
d
While you were doing that, though, someone else committed, revision 2,
which is:
foo:
a
b
bar:
c
You go to commit your changes, and svn tells you you can't, because
you are out of date. So you have to svn update. Now you have:
foo:
a
b
bar: CONFLICT
>>>>
b
d
====
c
<<<<
Now, how do you roll back to what you had before you updated? The
state of "foo" has been lost forever by the successful merge.
For example, git checkout filename is equivalent to svn revert filename.
Annoying, maybe, but this is user error, not design error. With git, if I want to losslessly discard my working copy, I can just "git stash". If I want to losslessly update my svn working copy, though, I have to make a copy myself, and then manage the copy.
By your logic, "rm" is flawed because it doesn't ask for confirmation when you pass -f instead of -i. Well, yeah. Sorry.
You didn't lose any data in that scenario. Every line of code you wrote still exists in those files. The problem is that you are in a conflicted state that must be manually resolved. Unfortunately, making updates atomic across a branch has disadvantages. For example, svn lets you update individual files or directories instead of the whole branch. If you want to avoid this pitfall in the future, run "svn merge --dry-run -r BASE:HEAD ." before a real update. (I wish svn update had a --dry-run flag. Just because git is bad doesn't mean svn is perfect.)
Also, your scenario is extremely unlikely. I've used svn for 5 years and I've encountered that problem once. It was for a binary file. Two versions of the same image. It's not very often that two developers create a new file with the same name at practically the same time. It's even less often that those files can be properly merged.
By your logic, "rm" is flawed because it doesn't ask for confirmation when you pass -f instead of -i. Well, yeah. Sorry.
$ git checkout blah
This command either switches to branch blah or it erases all uncommitted changes in a directory or file named blah. Without more information, you can't tell. I find that frustrating and annoying. Your analogy would be more accurate if rm somename was the equivalent of apt-get update, and rm othername was rm -fr othername. Oh, and somename is never tab-completed but othername is.
> You didn't lose any data in that scenario. Every line of code you wrote still exists in those files. The problem is that you are in a conflicted state that must be manually resolved.
I think that the point is that file 'foo' has already been merged, regardless of the conflict in file 'bar'. There is no way for you to revert to the pre-update state. In git, your previous commit still exists in the objects store even if it is no longer connected to the tree. And garbage collection won't even clean it out right away because it is still in the reflog ('git reflog'). The point being that once something is committed, it's permanently (barring garbage collection) in the repository. Whenever you make a change to a commit, a new commit is created, some pointers are changed, and the old commit still remains in the repository.
> Oh, and somename is never tab-completed but othername is.
Responsibility for the tab-completion falls squarely on your shell (or where ever you got the tab-completion setup from). Don't point your finger at git and say, "git sucks because bash tab-completion screwed me up." Neither rm nor git can control how your shell bothers to determine tab-completion.
Still, it can't be right that "get checkout foo" does one of two COMPLETELY different things depending on whether or not there is a file called foo in the current directory. Surely one of those two commands should have a different name.
I've always felt like `git branch` could be the one to switch branches (since it's used to create them too). But `git checkout -b` also creates branches... I think semantically checkout is the right command for this.
It's never come up as a problem because I tend to know what files are in my project and I also tend to know what it is I'm about to/want to do. I very rarely switch branches with a dirty working copy anyway and my branches are never named even remotely close to what files are named (by coincidence, I suppose, but I name branches after: 'releases' which have names like "2.2.2"; 'bug fixes' which have names like "bug2598" or 'features' which have names like "dashboard-rewrite" and "chunk-load-thumbnails").
Here's another crazy idea: don't run 'git checkout ...' on a dirty work tree. Problem solved.
Another one: don't reuse filenames as branch-names.
To be honest: I have the same problem with careless invocations of 'rm' ruining my day but when I'm muttering curses it is at my lazyness/stupidity and not at bash completions or the behavior of 'rm'
Honestly, it's annoying that we all use single-version filesystems. It was a good idea back when computer storage consisted of a big rotating metal drum and a major government could only afford 1MB of storage.
Now that 1TB is like $70, we should just keep every filesystem state around. Maybe not forever, but so that "rm -rf *" is just a "gah, that's not what i wanted!" moment instead of a "the rest of my day is ruined" moment.
Premature optimization is the root of all data loss.
Well, this begs the question, why not just let git do all that diff-packing and 99% of the conflict resolution crap for you?
As long as you don't mind spending a weekend or a few evenings reading about the new tool and how it works (like any new addition to your skillset), that is.
I'm afraid the git clone behaviour is unusable for the svn repository I work from. A clean pulled tree is over 1GB at the moment I believe, and contains many binary files (static libraries etc.) that aren't rebuilt that often, but often enough to make the repository as a whole very large. I don't know exactly how large it is, but it's large enough that it would take many weeks to download over VPN. Just getting a fresh tree takes over 24 hours as it is.
As for diff packing, it's as simple as this on the bash command line:
collect-patch $(now)-hint.patch foo/{bar,baz}
The collect-patch script will do an svn diff over foo/bar and foo/baz from the root of my working directory, and deposit the results in a file in my current directory (e.g. 2010-05-13_15-50-whatever.patch).
Then, I can revert local changes:
revert 2010-05-13_15-50-whatever.patch
(This reverts changes to the files listed as modified in the patch.) Because it's a file, I get completion on that. And to apply (e.g. after an svn update):
apply 2010-05-13_15-50-whatever.patch
If things get more complicated, and there are conflicts in the lines modified by my patch and the updated source (which is less than 1% of the time), then I do:
manual-merge 2010-05-13_15-50-whatever.patch
That uses a tool (BeyondCompare 3) to do three-way merging between the original revision of my patch, my local edit to the original revision, and the new revision that's been gotten locally. BC3 can auto-merge for all non-conflicting edit sections, but be interactive with a nice diff display and editing if there's a conflict (/automerge and /reviewconflicts arguments to bcomp).
I'm sure I could migrate this scheme onto an analogous git scheme that could keep track of the merges better, but in practice this scheme is enough - and because I wrote it, I'm intimately familiar with its mechanics, so I can extend it, and I'm not at risk of file loss, etc.
I don't think new git users migrating from svn actually understand that they can rollback any changes git makes. If a pull goes bad and you don't want to deal with it, just reset to your last head. All is forgotten until you feel like fixing it. And no data is ever lost.
(svn loses data by merging everything it can with your uncommitted changes, and then barfing when it can't do the merge. You're left with a bunch of conflicted file, and a bunch of merged files, with no way to ever get the unmerged files again. With git, this simply cannot happen. Git will not touch untracked files or unstaged changes, it will just die. And what it does automatically merge can be unmerged with reset. Or, you can commit the conflict markers to a separate branch, deal with it later, undo that commit, and move on with your life. Git makes easy things a little harder, but hard things easy. Subversion makes anything except hair loss very difficult.)
Oh, and also, the OP really wants his tree to end up like:
This can be achieved with "git pull --rebase" (or automagically with the config key branch.<name>.rebase = true). That is what Subversion does, modulo the ability to revert to your original branch.