threads / discuss / 25506

Following history of a copied file from another indirect branch

Subject: Following history of a copied file from another indirect branch

## tl;dr

4 messages between Oct 21, 2010 and Oct 22, 2010.

replies: 3people: 3as markdown or json

Joshua Jensen· Oct 21, 2010, 18:47 UTC · lore

It has become a necessity to copy a file from one long-lived branch to another. It is not possible to merge the branches at this time.

I would like to have 'git gui blame' follow the copy back through its original history, but I don't believe Git has metadata for storing this. Something along the lines of a 'followparent' in the commit object, for instance, would allow the revision walking code to wander the history down an alternate line.

By comparison, integrates work at a file level in Perforce. That means I can integrate a file from one branch to another, and parentage is stored such that I can follow the file back through its history.

Are there any facilities to do this now?
Thanks!
Josh
Brian Gernhardt· Oct 21, 2010, 19:39 UTC · re: Joshua Jensen · lore

Re: Following history of a copied file from another indirect branch

On Oct 21, 2010, at 2:47 PM, Joshua Jensen wrote:
> It has become a necessity to copy a file from one long-lived branch to another.  It is not possible to merge the branches at this time.
> 
> I would like to have 'git gui blame' follow the copy back through its original history, but I don't believe Git has metadata for storing this.  Something along the lines of a 'followparent' in the commit object, for instance, would allow the revision walking code to wander the history down an alternate line.
Git stores no per-file metadata.  The closest we come is .gitattributes and .gitignore.
> By comparison, integrates work at a file level in Perforce.  That means I can integrate a file from one branch to another, and parentage is stored such that I can follow the file back through its history.
> 
> Are there any facilities to do this now?
Git simply does not have the idea of the history of a file.  Nothing in git will help merge "just a file" from one branch to another.  Either we have merged the two commits or not.
HOWEVER...
You can use git-filter-branch to create a new branch that contains only that single file and only the commits that affected it.  Something like the following (untested):
	# Merging "file" from branch "src" to branch "dest"
	git checkout -b temp src
	git filter-branch --prune-empty --index-filter="git read-tree --empty; git add file"
	# Since you describe the branch as "long-running", I'd suspect you'll have to wait a while here.
	git checkout dest
	git merge temp
	git branch -d temp
	git branch -D refs/original/temp
This will go faster if you have a ramdisk/tmpfs to perform the filtering in.  (git-filter-branch is very I/O intensive.)  Something like the following in place of the `git filter-branch` invocation above:
	mkdir /tmp/filter-branch # Assuming /tmp is tmpfs or similar
	git filter-branch -d /tmp/filter-branch --prune-empty --index-filter="git read-tree --empty; git add file"
	rm -rf /tmp/filter-branch
You could use --msg-filter to add the SHA-1 of the original commits to the "file history" branch.  Something like --msg-filter='cat;echo;echo From: $GIT_COMMIT'
I would recommend using cherry-pick to pull any further changes to the file across branches (be careful of commits that touch more than that file!).  I think git-filter-branch could be used to keep the one file branch up to date, but that is likely more effort than it's worth.  I would specifically advise against merging the single file branch into both "src" and "dest", as I think any later merge of the two would find these commits as a merge-base.
~~ Brian Gernhardt
Joshua Jensen· Oct 21, 2010, 20:06 UTC · re: Brian Gernhardt · lore

Re: Following history of a copied file from another indirect branch

----- Original Message -----
From: Brian Gernhardt
Date: 10/21/2010 1:39 PM
Show 10 quoted lines
> On Oct 21, 2010, at 2:47 PM, Joshua Jensen wrote:
>> It has become a necessity to copy a file from one long-lived branch to another.  It is not possible to merge the branches at this time.
>>
>> I would like to have 'git gui blame' follow the copy back through its original history, but I don't believe Git has metadata for storing this.  Something along the lines of a 'followparent' in the commit object, for instance, would allow the revision walking code to wander the history down an alternate line.
> Git stores no per-file metadata.  The closest we come is .gitattributes and .gitignore.
>
>> By comparison, integrates work at a file level in Perforce.  That means I can integrate a file from one branch to another, and parentage is stored such that I can follow the file back through its history.
>>
>> Are there any facilities to do this now?
> Git simply does not have the idea of the history of a file.  Nothing in git will help merge "just a file" from one branch to another.  Either we have merged the two commits or not.

I'm not super interested in per file merging (which is a great concept, works well in Perforce, but is irrelevant here). I merely want to preserve the original parentage so facilities like blame (ultimately rev-list?) can walk the extended history. I'm fine even passing in a flag. I do not care in preserving the original parentage for purposes of merging.

> You can use git-filter-branch to create a new branch that contains only that single file and only the commits that affected it.  Something like the following (untested):
>
> I would recommend using cherry-pick to pull any further changes to the file across branches (be careful of commits that touch more than that file!).  I think git-filter-branch could be used to keep the one file branch up to date, but that is likely more effort than it's worth.  I would specifically advise against merging the single file branch into both "src" and "dest", as I think any later merge of the two would find these commits as a merge-base.
Thanks for the info.

The problem with using cherry-pick is that the commits in question contain more than one file. Perhaps the individual file should have been committed separately, but the damage was long ago done.

git format-patch --stdout HEAD..otherbranch -- the/filename | git am or git diff HEAD..otherbranch -- the/filename | git apply

Seem to be the appropriate methods of copying the file over with fake history or squashed together.

Josh
Johannes Sixt· Oct 22, 2010, 06:35 UTC · re: Joshua Jensen · lore

Re: Following history of a copied file from another indirect branch

Am 10/21/2010 20:47, schrieb Joshua Jensen:
Show 8 quoted lines
> It has become a necessity to copy a file from one long-lived branch to
> another.  It is not possible to merge the branches at this time.
> 
> I would like to have 'git gui blame' follow the copy back through its
> original history, but I don't believe Git has metadata for storing this. 
> Something along the lines of a 'followparent' in the commit object, for
> instance, would allow the revision walking code to wander the history down
> an alternate line.

You can branch off one commit form that long-lived branch that undoes everything except the file F you are interested in since the last merge-base. Then you merge that single commit into the other branch.

   ---o--o--B     <- long-lived
     /  /    \
    /  /      U   <- the-file
   /  /        \
 -o--A----------M <- master (the other branch)

i.e. 'git diff A..B' shows a lot of changes, but 'git diff A..U' shows only changes to the file you are interested in. 'git diff -R B..U' differs from 'git diff A..B' only in the changes to the file you are intersted in.

When you later find that you need new changes to F that were made on long-lived, but you still cannot merge long-lived, then you can merge long-lived into the-file (resolve conflicts by removing the conflicted files and also remove newly added files), and then you merge the-file into master again.

WARNING: When you later merge long-lived into master, the merge will lose
all changes made on long-lived. You work it around by temporarily grafting
away the merge parents that point to commits listed by
long-lived..the-file. After you complete the merge, you can remove the
grafts again.
-- Hannes

← back to recent threads