Log in

assert(♥)

July 11th, 2008

As might be obvious from my previous post, I don’t yet grok git. (It took three tries over about a week to figure out what I posted there.) My other major question as a dual git/subversion user is how do I svn copy in git? I don’t see that adequately answered anywhere.

The most common use of svn copy is to branch, which is precisely what I don’t mean here. git seems to promote branches to a first-order concept, in that the entire git repository exists across branches, and there are specific commands for branching and merging. You can’t have a nonstandard trunk/branch/tag hierarchy like you do sometimes in subversion, because there is no hierarchy. git branches are completely orthogonal to your file structure.

Git From the Bottom Up suggests (perhaps a little facetiously) phrasing your problem in git’s language in order to understand:

Understanding commits is the key to grokking Git. You’ll know you have reached the Zen plateau of branching wisdom when your mind contains only commit topologies, leaving behind the confusion of branches, tags, local and remote repositories….

In these terms, you find branches are really names for other commits besides the master head main trunk commit. It’s not really that branches are first-order things, but that branches are names for commits instead of files. Either way, they’re completely orthogonal to the filesystem.

Looking again just now for the answer to my question, I found this thread, which illustrates git’s current position on copying, and how it’s contrary to this second use of svn copy that I’m trying to figure out:

svn copy::
Duplicate something in working copy or repository, remembering history.
cp A B; git add B::
Git doesn’t have a direct equivalent of svn copy. It’s arguable whether
it needs it once the user knows they can git-add so easily.

svn copy is more like git checout -b, i.e. it’s primary purpose is not
to “copy” things, it is to create branches. You generally do not copy
code (I hope).

Well, in fact, I often do copy code with svn, because I want to fork a file with history. Often I discover when I’m working on (say) some class, I’ve accreted unrelated functionality around the class’s real work, and I need to separate it out. Obviously I can do that just fine and check both parts in, but if I naïvely fork one file in twain—by doing a real file copy and adding the new file, say—the new one will lose all its history. The first commit git will know about for it is the one where it appears fully formed from Zeus’ head, though it happens to share an equivalent blob with some other file in the commit.

In git terms, you can see why it’s not obvious how to copy with history: to duplicate svn copy AB, you want git to understand when you ask about B’s history to include all of A’s previous commits. It’s as though you want to change all A’s commits retroactively to include B, sort of. It’s more about the behavior of the tools than anything you can articulate in a git repository’s data.

So I looked for behavior: lo and behold, Andy Parkin’s message above notes that git can “detect copies after-the-fact,” and I guess he means for example git log’s -C and --find-copies-harder options. According to the manual, this seems to be exactly the behavior I need:

-C

Detect copies as well as renames. See also --find-copies-harder.

--find-copies-harder

For performance reasons, by default, -C option finds copies only
if the original file of the copy was modified in the same
changeset. This flag makes the command inspect unmodified files
as candidates for the source of copy. This is a very expensive
operation for large projects, so use it with caution. Giving
more than one -C option has the same effect.

If only it worked that way. See the terminalcast I did showing what I mean: once I duplicate the file, svn log doesn’t show fred’s initial commit in wilma’s history, even with the -C or --find-copies-harder flags.

I would be delighted to be wrong, but as far as I can tell, it’s not possible to fork files in git, while it’s trivial with svn copy.