5

I wanted to make my original complicated question much simpler.

I have the following file in a repo:

E:\\a\b\FooOld\foo.java

I copied the file to another directory a few weeks ago with a new repo:

E:\\c\d\FooNew\foo.java

Is there a way to copy the file history of foo.java from the old repo to the new repo?

Hong
  • 17,643
  • 21
  • 81
  • 142
  • 1
    No, the "history" does not belong to the file, it belongs to the repo/commit. It makes no sense to move the "history of a file" between repos, because it does not belong to the file it belongs to the repo, GIT commits are complete copies of the entire repo, not a single files changes – Liam Dec 17 '18 at 16:47
  • Thank you for the insight. Would the word "merge" be more appropriate? In other words, can the history of file foo.java of repo A be merged with the history of file foo.java in repo B? – Hong Dec 17 '18 at 16:53
  • 1
    No, you cannot merge a **file** (full stop). You can merge a repo but not a file in a repo. The lowest level that merges happen is repo, there is nothing below this – Liam Dec 17 '18 at 17:09
  • Thank you for the further elucidation. I have accepted the answer that is in line with your comments. – Hong Dec 17 '18 at 19:16

2 Answers2

8

It can be done, in two big steps. Let's say you have a repo A that contains files that you want to be added on a different repo B:

  1. Use git filter-branch to create a clone of repo A containing only the history of the files you want to preserve.

To achieve this, you can take a look at this answer if you need to preserve history of moves and renames, otherwise there are plenty of other answers on Stack Overflow that does that in a simpler way using git filter-branch. Or you can use filter-repo, which seems to be a much faster and simpler approach to re-write history.

Important: Remember to run git filter-branch on a clone of your repo. I also recommend running git remote rm origin on the clone so there is no chance of pushing this stripped version to origin by accident.

  1. Merge stripped A into B. In repo B, do this:

    2.1. Create a remote connection from B to A:

    git remote add repo-A <git repository stripped A directory>;

    2.2. Pull using the option --allow-unrelated-histories. Providing both repos are using the master branch, the following command will merge one repo into another:

    git pull repo-A master --allow-unrelated-histories

    2.3. Remove the remote connection:

    git remote rm repo-A

Roberto
  • 11,557
  • 16
  • 54
  • 68
2

This is literally impossible, for the simple reason that there is no such thing as "file history" in Git. In Git, the history is simply the set of all commits. If you add commits, you add history. Whatever commits there are, those are the history. And, each commit represents a complete snapshot of all of your files (well, all the files that are in that commit, but that's a bit redundant).

You can ask git log to show you commits that modify some particular file. The way this works is that git log traipses along through history—backwards, through each commit, one at a time, in other words—and compares all the files in this commit to all the files in the previous commit. If the one specific file you're curious about has changed, git log now shows this one commit. Then it moves on—or rather, back—to the previous commit, whether or not it showed this one commit, and shows, or doesn't, that commit using the same rule, and so on.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you. I can see the reason easily when thinking of the history as a set of commits as you suggested. In theory, the latest version of old foo.java and the original version of the new foo.java can be compared to see if they are different to see if there is a need to make commit to link them, then the commits of the two repos can be chained together (i.e. merged). – Hong Dec 17 '18 at 19:26
  • 1
    Right: if you have all the old commits and all the new ones, and a merge between them, Git will walk both sets of commits. There's an annoyance here because `git log` (in particular, `git log --follow`) isn't really quite smart enough about going back along both segments of history, but someday `git log` might be smarter. – torek Dec 17 '18 at 19:38
  • 2
    "literally impossible" is incorrect and unhelpful. Obviously "file history" exists in concept and git has the necessary information; it knows the state of each file at every commit, including dates/times of changes made (as you explain yourself re `git log`). If you are willing to rewrite history in the new repo, you can add the file and all its changes elsewhere. [Roberto's answer below](https://stackoverflow.com/a/59448657/1705163) provides some options. – gens Apr 28 '20 at 04:52
  • @gens: no, a specific file history literally doesn't exist. The answers you get from `git log` depend on how you ask. You could claim that multiple different conceptual file histories exist simultaneously, but this does not help solve the problem. Roberto's answer provides filtered *commits*, which is one of the ways to ask a different question. The point here is that you must figure out which question you want, rather than blindly picking an answer that might not make sense in the end. – torek Apr 28 '20 at 05:56