Hong
Hong

Reputation: 18531

How to copy a single file's history from an old Git repo to a new repo?

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?

Upvotes: 6

Views: 2697

Answers (3)

ChrisZZ
ChrisZZ

Reputation: 2191

The basic idea

  • Create a new repoo
  • Create a branch based on branch that contains commit history of your target file
  • Remove other files in current working copy
  • Remove other files from git commit history, by using git filter-repo command.

The detail commands

create an empty repo:

mkdir ~/work/new_repo
cd ~/work/new_repo
git init

add location of old repo as new repo's remote

# it can be a URL
git remote add old_upstream https://github.com/xxx/yyy
# or an local directory
git remote add old_upstream D:/work/old_repo

create branch based on you interested files/directories

git fetch
git checkout -b  my_branch  old_upstream/some_branch

delete all the files in current working, except the files you interest

git rm -rf build # remove the directory `build`
git rm -f README.md  # remove the file `README.md`
git rm -rf .  # removes all files. Do this if you know what you're doing.

Now, the working directory is clean, but the commit history still contains some files you won't want to see. let's remove them.

install git filter-repo

python -m pip install git-filter-repo

Remove git commit history items that contain specified file/directories

git filter-repo --invert-paths --path some_directory --force
git filter-repo --invert-paths --path some_file.md --force

(You may use VSCode's git-lens extension, or other tools, to compare the count of git commit history before and after each git filter-repo command. The count number decrease as we expected).

Upvotes: 0

Roberto
Roberto

Reputation: 11953

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

Upvotes: 9

torek
torek

Reputation: 490078

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.

Upvotes: 1

Related Questions