git - How to checkout a folder from another branch with its history? - Stack Overflow

admin2025-05-01  0

I'm using a static page generator (like Hugo, Jekyll, ...). Inside my repository there is the source code for the static page generator, but also the content, that is used when generating the pages.

I want to be able to build the pages with a newer version of the content, than the source code. So I clone the repository with branch A, but want to build it with the content from branch B.

  • <root>/ from branch A
  • <root>/src from branch A
  • <root>/README.md from branch A
  • <root>/content from branch B

One important aspect is, that I need the history of everything corresponding to their branch. Some files in <root>/content exist on branch A and B, but I only want the history from branch B.

When there is a file <root>/content/blog/post-1.md and I do git log content/blog/post-1.md the history should be taken only from branch B.

I've tried many things, but I fail in having the correct history present.

  • git checkout origin/branch-b -- content
    then git log content/blog/post-1.md displays the history present in branch A instead of branch B.
  • git worktree add ../other content
    ln -s ../other/content content
    then git log content/blog/post-1.md displays the history present in branch A instead of branch B. Git doesn't seem to follow symlinks.
  • git subtree add --prefix=content content content
    then I get the error fatal: prefix 'src/utils' already exists.. When I delete this folder I eventually can add the subtree, but it will have the history from branch A.
  • git log --follow origin/branch-b ... can be an option, but with this I need to change the git integration of the static page generator. This depends on a third party plugin, that doesn't support --follow. So I'm looking for a seamless solution.

Maybe doing it the other way around would be a solution? Cloning branch B and overlay everything from branch A, as I don't depend on the history of branch A?

I'm using a static page generator (like Hugo, Jekyll, ...). Inside my repository there is the source code for the static page generator, but also the content, that is used when generating the pages.

I want to be able to build the pages with a newer version of the content, than the source code. So I clone the repository with branch A, but want to build it with the content from branch B.

  • <root>/ from branch A
  • <root>/src from branch A
  • <root>/README.md from branch A
  • <root>/content from branch B

One important aspect is, that I need the history of everything corresponding to their branch. Some files in <root>/content exist on branch A and B, but I only want the history from branch B.

When there is a file <root>/content/blog/post-1.md and I do git log content/blog/post-1.md the history should be taken only from branch B.

I've tried many things, but I fail in having the correct history present.

  • git checkout origin/branch-b -- content
    then git log content/blog/post-1.md displays the history present in branch A instead of branch B.
  • git worktree add ../other content
    ln -s ../other/content content
    then git log content/blog/post-1.md displays the history present in branch A instead of branch B. Git doesn't seem to follow symlinks.
  • git subtree add --prefix=content content content
    then I get the error fatal: prefix 'src/utils' already exists.. When I delete this folder I eventually can add the subtree, but it will have the history from branch A.
  • git log --follow origin/branch-b ... can be an option, but with this I need to change the git integration of the static page generator. This depends on a third party plugin, that doesn't support --follow. So I'm looking for a seamless solution.

Maybe doing it the other way around would be a solution? Cloning branch B and overlay everything from branch A, as I don't depend on the history of branch A?

Share Improve this question edited Jan 2 at 16:45 Robin asked Jan 2 at 14:42 RobinRobin 8,5489 gold badges65 silver badges108 bronze badges 10
  • It sounds like it would be a better set up to manage the source code in a separate project and then only bring in the version you wish to use to generate your content at build/deploy time in your content project. I don't think doing what you are proposing is a great idea as your branches are going to end up spaghetti. – JNevill Commented Jan 2 at 15:25
  • Yes, but then continuous integration and delivery becomes harder, as changes in either of the projects cannot trigger easily common workflows. – Robin Commented Jan 2 at 15:30
  • 1 Reusable workflows like github actions would be a solution for that. This is a pretty common pattern where you have platform/source and content/artifact/code managed in two repos. One I'm most familiar with is Airflow where one would generally manage platform+airflow in one project and their dags in a second. A change to either would prompt some shared build/deploy steps. The small added complexity in build/deploy more than offsets the complexity in your branching strategy and some likely really ugly merge conflicts and other git oddities. – JNevill Commented Jan 2 at 15:55
  • 2 The "including history" requirement is probably going to be really tricky given git's model: in git, a file or directory does not have history, the entire repository has history. Running git log with a file path is still showing the history of the repository, it's just skipping commits which didn't change that file path. As the manual puts it: "Show only commits that are enough to explain how the files that match the specified paths came to be." – IMSoP Commented Jan 2 at 16:41
  • 1 @phd yes, it really does the trick and only adds about 2s on our continuous integration. git worktree add ../release branch-a and then rsync -a --exclude '.git' --exclude 'content' --delete ../release/ ./. – Robin Commented Jan 2 at 17:29
 |  Show 5 more comments

1 Answer 1

Reset to default 2

From your comments, it looks like you want a way to restore "everything but content/" from branchA to branchB

Here is a way to do that:

  1. make sure to have a clean repository, commit or stash or delete your non-committal changes

  2. Run the following commands:

# switch to branchB
git switch branchB

# restore everything from branchA,
# then re-restore content/ from branchB
git restore -SW -s branchA -- .
git restore -SW -s branchB -- content/

# check that everything is to your liking ...

# commit the result
git commit

One word of caution: the -W option (short for --worktree) of git restore can delete changes from your disk which haven't been stored in git yet (exactly like git reset --hard), this is why it is important to work on a clean repo to avoid deleting things accidentally.

转载请注明原文地址:http://anycun.com/QandA/1746113947a91862.html