如何将现有的 Git 存储库导入到另一个存储库中?

发布于 2024-08-11 00:43:23 字数 607 浏览 5 评论 0原文

我在名为 XXX 的文件夹中有一个 Git 存储库,并且有第二个名为 YYY 的 Git 存储库。

我想将 XXX 存储库作为名为 ZZZ 的子目录导入到 YYY 存储库中,并添加所有 XXX 的将历史记录更改为YYY

之前的文件夹结构:

├── XXX
│   ├── .git
│   └── (project files)
└── YYY
    ├── .git
    └── (project files)

之后的文件夹结构:

YYY
├── .git  <-- This now contains the change history from XXX
├──  ZZZ  <-- This was originally XXX
│    └── (project files)
└──  (project files)

可以这样做吗,或者我必须求助于使用子模块吗?

I have a Git repository in a folder called XXX, and I have second Git repository called YYY.

I want to import the XXX repository into the YYY repository as a subdirectory named ZZZ and add all XXX's change history to YYY.

Folder structure before:

├── XXX
│   ├── .git
│   └── (project files)
└── YYY
    ├── .git
    └── (project files)

Folder structure after:

YYY
├── .git  <-- This now contains the change history from XXX
├──  ZZZ  <-- This was originally XXX
│    └── (project files)
└──  (project files)

Can this be done, or must I resort to using sub-modules?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

予囚 2024-08-18 00:43:24

这是立即生效的脚本。

#!/bin/bash -xe
# script name: merge-repo.sh
# To merge repositories into the current.
# To see the log of the new repo use 'git log --follow -- unprefixed-filename'
# So if the file is repo/test.cpp use 'git log --follow -- test.cpp'
# I'm not sure how this will work when two files have the same name.
#
# `git branch -a` will show newly created branches.
# You can delete them if you want.
merge_another() {
    repo="$1" # url of the remote repo
    rn="$2"   # new name of the repo, you can keep the same name as well.
    git remote add ${rn} ${repo}
    git fetch ${rn}
    git merge -s ours --no-commit --allow-unrelated-histories ${rn}/master
    git read-tree --prefix=${rn}/ -u ${rn}/master
    git commit -m "Imported ${rn} as a subtree."
    git pull -s subtree ${rn} master
}

merge_another $1 $2

运行脚本。转到您想要合并其他存储库的存储库,然后运行脚本。

cd base-repo
./merge-repo.sh [email protected]:username/repo-to-be-merged.git repo-to-be-merged-new-name

现在将 master 分支上的更改推送到远程/原点。根据您想要执行的操作,可能不需要执行此步骤。

git push origin master

Here is the script that will work right off the bat.

#!/bin/bash -xe
# script name: merge-repo.sh
# To merge repositories into the current.
# To see the log of the new repo use 'git log --follow -- unprefixed-filename'
# So if the file is repo/test.cpp use 'git log --follow -- test.cpp'
# I'm not sure how this will work when two files have the same name.
#
# `git branch -a` will show newly created branches.
# You can delete them if you want.
merge_another() {
    repo="$1" # url of the remote repo
    rn="$2"   # new name of the repo, you can keep the same name as well.
    git remote add ${rn} ${repo}
    git fetch ${rn}
    git merge -s ours --no-commit --allow-unrelated-histories ${rn}/master
    git read-tree --prefix=${rn}/ -u ${rn}/master
    git commit -m "Imported ${rn} as a subtree."
    git pull -s subtree ${rn} master
}

merge_another $1 $2

To run the script. Go to the repo where you want the other repo to be merged, and run the script.

cd base-repo
./merge-repo.sh [email protected]:username/repo-to-be-merged.git repo-to-be-merged-new-name

Now push the changes on the master branch to remote/origin. This step may not be required depending on what you are trying to do.

git push origin master
玻璃人 2024-08-18 00:43:24

在我的例子中,我只想从其他存储库(XXX)导入一些文件。子树对我来说太复杂了,其他解决方案不起作用。这就是我所做的:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

这为您提供了一个以空格分隔的所有提交列表,这些提交影响我想要以相反顺序导入的文件(ZZZ)(您可能还需要添加 --follow 来捕获重命名)。然后我进入目标存储库(YYY),将另一个存储库(XXX)添加为远程存储库,从中进行提取,最后:

git cherry-pick $ALL_COMMITS

将所有提交添加到您的分支,因此您将拥有所有文件及其历史记录和可以对它们做任何您想做的事情,就像它们一直在这个存储库中一样。

I wanted to import only some files from the other repository (XXX) in my case. The subtree was too complicated for me and the other solutions didn't work. This is what I did:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

This gives you a space-separated list of all the commits that affect the files I wanted to import (ZZZ) in reverse order (you might have to add --follow to capture renames as well). I then went into the target repository (YYY), added the other repository (XXX) as remote, did a fetch from it and finally:

git cherry-pick $ALL_COMMITS

which adds all the commits to your branch, you'll thus have all the files with their history and can do whatever you want with them as if they've always been in this repository.

花开浅夏 2024-08-18 00:43:24

请参阅本文中的基本示例< /a> 并考虑存储库上的此类映射:

  • A <-> YYY
  • B <-> XXX

在本章描述的所有活动之后(合并后),删除分支 B-master

$ git branch -d B-master

然后,推送更改。

这对我有用。

See Basic example in this article and consider such mapping on repositories:

  • A <-> YYY,
  • B <-> XXX

After all activity described in this chapter (after merging), remove branch B-master:

$ git branch -d B-master

Then, push changes.

It works for me.

反话 2024-08-18 00:43:24

我当时的情况是在寻找 -s thoses 但当然,这种策略不存在。我的历史是,我在 GitHub 上分叉了一个项目,现在由于某种原因,我的本地 master 无法与 upstream/master 合并,尽管我没有进行本地更改到这个分支。 (真的不知道那里发生了什么——我猜上游可能在幕后做了一些肮脏的推动?)

我最终做的是

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

所以现在我的 master 再次与 同步>upstream/master (您可以对您也想类似同​​步的任何其他分支重复上述操作)。

I was in a situation where I was looking for -s theirs but of course, this strategy doesn't exist. My history was that I had forked a project on GitHub, and now for some reason, my local master could not be merged with upstream/master although I had made no local changes to this branch. (Really don't know what happened there -- I guess upstream had done some dirty pushes behind the scenes, maybe?)

What I ended up doing was

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

So now my master is again in sync with upstream/master (and you could repeat the above for any other branch you also want to sync similarly).

风苍溪 2024-08-18 00:43:24

我可以建议另一个解决方案(替代 git-submodules)针对您的问题 - gil(git 链接)工具

它允许描述和管理复杂的 git 存储库依赖项。

它还提供了 git 递归子模块依赖问题的解决方案。

考虑您有以下项目依赖项:
示例 git 存储库依赖关系图

然后您可以使用以下命令定义 .gitlinks 文件存储库关系描述:

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

每行描述 git 链接,格式如下:

  1. 存储库的唯一名称
  2. 存储库的相对路径(从 .gitlinks 文件的路径开始)
  3. 将在 git clone 命令中使用的 Git 存储库
    存储库分支到 checkout
  4. 空行或以 # 开头的行不会被解析(被视为注释)。

最后,您必须更新根示例存储库:

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

因此,您将克隆所有必需的项目并以正确的方式将它们相互链接。

如果您想提交某个存储库中的所有更改以及子链接存储库中的所有更改,您可以使用单个命令来完成:

gil commit -a -m "Some big update"

拉取,推送命令的工作方式类似:

gil pull
gil push

Gil(git links)工具支持以下命令:

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

更多关于< a href="https://github.com/chronoxor/gil#recursive-submodules-problem" rel="nofollow noreferrer">git 递归子模块依赖问题。

I can suggest another solution (alternative to git-submodules) for your problem - gil (git links) tool

It allows to describe and manage complex git repositories dependencies.

Also it provides a solution to the git recursive submodules dependency problem.

Consider you have the following project dependencies:
sample git repository dependency graph

Then you can define .gitlinks file with repositories relation description:

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

Each line describe git link in the following format:

  1. Unique name of the repository
  2. Relative path of the repository (started from the path of .gitlinks file)
  3. Git repository which will be used in git clone command
    Repository branch to checkout
  4. Empty line or line started with # are not parsed (treated as comment).

Finally you have to update your root sample repository:

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

As the result you'll clone all required projects and link them to each other in a proper way.

If you want to commit all changes in some repository with all changes in child linked repositories you can do it with a single command:

gil commit -a -m "Some big update"

Pull, push commands works in a similar way:

gil pull
gil push

Gil (git links) tool supports the following commands:

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

More about git recursive submodules dependency problem.

手长情犹 2024-08-18 00:43:24

没有足够的代表来为 x-yuri 的答案添加评论,但它工作得很好并且保留了历史。
我正在使用两个工作本地存储库并收到此错误:

正在中止:拒绝破坏性地覆盖回购历史记录
这看起来不像是一个新的克隆。
(预计新鲜包装的回购)
请改为在新的克隆上进行操作。如果您仍想继续,请使用 --force。

我没有担心 --force 标志的含义,而是首先使用以下命令在本地克隆存储库:

cd tempDir
git clone <location of repo to be merged> --no-local

并将这个新克隆的副本用于 x-yuri 列出的一系列命令。
最后,在:git filter-repo --to-subdirectory-filter a中,a是您为要导入的存储库的根文件夹指定的名称。

Don't have enough rep to add a comment to x-yuri's answer, but it works beautifully and preserves history.
I was working with two working local repo's and received this error:

Aborting: Refusing to destructively overwrite repo history since
this does not look like a fresh clone.
(expected freshly packed repo)
Please operate on a fresh clone instead. If you want to proceed anyway, use --force.

Rather than worry about the implications of the --force flag, I cloned the repo locally first with:

cd tempDir
git clone <location of repo to be merged> --no-local

and used this freshly cloned copy for the series of commands that x-yuri laid out.
Lastly, in: git filter-repo --to-subdirectory-filter a, a is the name you are giving to the root folder for the repo that you will be importing.

羁拥 2024-08-18 00:43:24

我不知道有什么简单的方法可以做到这一点。您可以这样做:

  1. 使用 git filter-branch 在 XXX 存储库上添加 ZZZ 超级目录
  2. 将新分支推送到 YYY 存储库
  3. 将推送的分支与 YYY 的主干合并。

如果听起来很吸引人,我可以编辑细节。

I don't know of an easy way to do that. You COULD do this:

  1. Use git filter-branch to add a ZZZ super-directory on the XXX repository
  2. Push the new branch to the YYY repository
  3. Merge the pushed branch with YYY's trunk.

I can edit with details if that sounds appealing.

哭泣的笑容 2024-08-18 00:43:24

我认为你可以使用“git mv”和“git pull”来做到这一点。

我是一个公平的 git noob - 所以要小心你的主存储库 - 但我只是在临时目录中尝试过这个,它似乎有效。

首先 - 重命名 XXX 的结构,以匹配您希望它在 YYY 中的外观:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

现在 XXX 看起来像这样:

XXX
 |- ZZZ
     |- ZZZ

现在使用“git pull”来获取更改:

cd ../YYY
git pull ../XXX

现在 YYY 看起来像这样:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)

I think you can do this using 'git mv' and 'git pull'.

I'm a fair git noob - so be careful with your main repository - but I just tried this in a temp dir and it seems to work.

First - rename the structure of XXX to match how you want it to look when it's within YYY:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

Now XXX looks like this:

XXX
 |- ZZZ
     |- ZZZ

Now use 'git pull' to fetch the changes across:

cd ../YYY
git pull ../XXX

Now YYY looks like this:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
念﹏祤嫣 2024-08-18 00:43:23

可能最简单的方法是将 XXX 内容拉入 YYY 中的分支,然后将其合并到 master 中:

YYY 中:

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

我实际上只是用我的几个存储库尝试过这个,它有效。与 Jörg 的答案不同,它不会让您继续使用其他存储库,但我认为您无论如何都没有指定这一点。

注意:由于本文最初是在 2009 年编写的,因此 git 添加了下面答案中提到的子树合并。我今天可能会使用这个方法,当然这个方法仍然有效。

Probably the simplest way would be to pull the XXX stuff into a branch in YYY and then merge it into master:

In YYY:

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

I actually just tried this with a couple of my repos and it works. Unlike Jörg's answer it won't let you continue to use the other repo, but I don't think you specified that anyway.

Note: Since this was originally written in 2009, git has added the subtree merge mentioned in the answer below. I would probably use that method today, although of course this method does still work.

公布 2024-08-18 00:43:23

如果您想保留第二个存储库的确切提交历史记录,并因此保留将来轻松合并上游更改的能力,那么这就是您想要的方法。它会导致子树的未修改历史记录被导入到您的存储库中,再加上一次合并提交以将合并的存储库移至子目录。

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

您可以像这样跟踪上游更改:

git pull -s subtree XXX_remote master

Git 在进行合并之前自行计算出根在哪里,因此您无需在后续合并中指定前缀。

缺点是,在合并的历史记录中,文件没有前缀(不在子目录中)。因此,git log ZZZ/a 将向您显示除合并历史记录之外的所有更改(如果有)。您可以这样做:

git log --follow -- a

但这不会显示合并历史记录中的其他更改。

换句话说,如果您不更改存储库 XXXZZZ 的文件,那么您需要指定 --follow 和一个不带前缀的小路。如果您在两个存储库中更改它们,那么您有 2 个命令,其中没有一个命令显示所有更改。

2.9 之前的 Git 版本:您不需要将 --allow-unlated-histories 选项传递给 git merge

另一个答案中使用 read-tree 并跳过 merge -s ours 步骤的方法实际上与使用 cp 复制文件并提交结果没有什么不同。

原始来源来自 github 的“子树合并”帮助文章。还有另一个有用的链接

If you want to retain the exact commit history of the second repository and therefore also retain the ability to easily merge upstream changes in the future then here is the method you want. It results in unmodified history of the subtree being imported into your repo plus one merge commit to move the merged repository to the subdirectory.

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

You can track upstream changes like so:

git pull -s subtree XXX_remote master

Git figures out on its own where the roots are before doing the merge, so you don't need to specify the prefix on subsequent merges.

The downside is that in the merged history the files are unprefixed (not in a subdirectory). As a result git log ZZZ/a will show you all the changes (if any) except those in the merged history. You can do:

git log --follow -- a

but that won't show the changes other then in the merged history.

In other words, if you don't change ZZZ's files in repository XXX, then you need to specify --follow and an unprefixed path. If you change them in both repositories, then you have 2 commands, none of which shows all the changes.

Git versions before 2.9: You don’t need to pass the --allow-unrelated-histories option to git merge.

The method in the other answer that uses read-tree and skips the merge -s ours step is effectively no different than copying the files with cp and committing the result.

Original source was from github's "Subtree Merge" help article. And another useful link.

猫卆 2024-08-18 00:43:23

git-subtree 是一个专门针对将多个存储库合并为一个同时保留历史记录(和/或分割子树历史记录,尽管这似乎与这个问题无关)的用例而设计的脚本。自 1.7.11 版本起,它作为 git 树的一部分发布

要将版本 处的存储库 合并为子目录

,请使用 git subtree add< /code> 如下:

git subtree add -P <prefix> <repo> <rev>

git-subtree 实现 子树以更加用户友好的方式合并策略

对于您的情况,在存储库 YYY 内,您将运行:

git subtree add -P ZZZ /path/to/XXX.git master

缺点是在合并的历史记录中,文件没有前缀(不在子目录中)。因此,git log ZZZ/a 将向您显示除合并历史记录之外的所有更改(如果有)。您可以这样做:

git log --follow -- a

但这不会显示合并历史记录中的其他更改。

换句话说,如果您不更改存储库 XXXZZZ 的文件,那么您需要指定 --follow 和一个不带前缀的小路。如果您在两个存储库中更改它们,那么您有 2 个命令,其中没有一个命令显示所有更改。

更多信息请此处

git-subtree is a script designed for exactly this use case of merging multiple repositories into one while preserving history (and/or splitting history of subtrees, though that seems to be irrelevant to this question). It is distributed as part of the git tree since release 1.7.11.

To merge a repository <repo> at revision <rev> as subdirectory <prefix>, use git subtree add as follows:

git subtree add -P <prefix> <repo> <rev>

git-subtree implements the subtree merge strategy in a more user friendly manner.

For your case, inside repository YYY, you would run:

git subtree add -P ZZZ /path/to/XXX.git master

The downside is that in the merged history the files are unprefixed (not in a subdirectory). As a result git log ZZZ/a will show you all the changes (if any) except those in the merged history. You can do:

git log --follow -- a

but that won't show the changes other then in the merged history.

In other words, if you don't change ZZZ's files in repository XXX, then you need to specify --follow and an unprefixed path. If you change them in both repositories, then you have 2 commands, none of which shows all the changes.

More on it here.

回眸一笑 2024-08-18 00:43:23

Git 存储库本身有一个众所周知的实例,它在 Git 社区中统称为“有史以来最酷的合并"(在发送给 Git 的电子邮件中使用的主题行 Linus Torvalds 之后)描述此合并的邮件列表)。在这种情况下,gitk Git GUI 现在是 Git 本身的一部分,实际上曾经是一个单独的项目。 Linus 设法将该存储库合并到 Git 存储库中,其方式

  • 显示在 Git 存储库中,就好像它一直是作为 Git 的一部分开发的,
  • 所有历史记录都保持完整,并且
  • 仍然可以在旧存储库中独立开发,更改只需通过 git pull 编辑即可。

该电子邮件包含重现所需的步骤,但它不适合胆小的人:首先,Linus 编写了 Git,所以他可能比你或我更了解它,其次,这几乎是 5 年前的事了,从那时起,Git 已经有了很大的改进,所以也许现在更容易了。

特别是,我想现在在这种特定情况下人们会使用 gitk 子模块。

There is a well-known instance of this in the Git repository itself, which is collectively known in the Git community as "the coolest merge ever" (after the subject line Linus Torvalds used in the e-mail to the Git mailinglist which describes this merge). In this case, the gitk Git GUI which now is part of Git proper, actually used to be a separate project. Linus managed to merge that repository into the Git repository in a way that

  • it appears in the Git repository as if it had always been developed as part of Git,
  • all the history is kept intact and
  • it can still be developed independently in its old repository, with changes simply being git pulled.

The e-mail contains the steps needed to reproduce, but it is not for the faint of heart: first, Linus wrote Git, so he probably knows a bit more about it than you or me, and second, this was almost 5 years ago and Git has improved considerably since then, so maybe it is now much easier.

In particular, I guess nowadays one would use a gitk submodule, in that specific case.

违心° 2024-08-18 00:43:23

让我使用名称 a (代替 XXXZZZ)和 b (代替 YYY),因为这使得描述更容易阅读。

假设您想要将存储库 a 合并到 b (我假设它们彼此相邻):

cd a
git filter-repo --to-subdirectory-filter a
cd ..
cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a

为此,您需要 git-filter-repo 已安装(filter-branch不鼓励)。

合并 2 个大存储库,将其中一个存储库放入子目录的示例: https://gist.github .com/x-yuri/9890ab1079cf4357d6f269d073fd9731

更多信息此处

Let me use names a (in place of XXX and ZZZ) and b (in place of YYY), since that makes the description a bit easier to read.

Say you want to merge repository a into b (I'm assuming they're located alongside one another):

cd a
git filter-repo --to-subdirectory-filter a
cd ..
cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a

For this you need git-filter-repo installed (filter-branch is discouraged).

An example of merging 2 big repositories, putting one of them into a subdirectory: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

More on it here.

別甾虛僞 2024-08-18 00:43:23

最简单的方法是使用 git format-patch。

假设我们有 2 个 git 存储库 foobar

foo 包含:

  • foo.txt
  • .git

bar 包含:

  • bar.txt
  • .git

我们希望最终 foo 包含 >bar 历史记录和这些文件:

  • foo.txt
  • .git
  • foobar/bar.txt

所以要这样做:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

如果我们想重写 bar 的所有消息提交,我们可以这样做,例如在 Linux 上:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

这将添加“[ bar] " 在每个提交消息的开头。

The simple way to do that is to use git format-patch.

Assume we have 2 git repositories foo and bar.

foo contains:

  • foo.txt
  • .git

bar contains:

  • bar.txt
  • .git

and we want to end-up with foo containing the bar history and these files:

  • foo.txt
  • .git
  • foobar/bar.txt

So to do that:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

And if we want to rewrite all message commits from bar we can do, eg on Linux:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

This will add "[bar] " at the beginning of each commit message.

丘比特射中我 2024-08-18 00:43:23

该功能将远程仓库克隆到本地仓库目录,合并后所有提交将被保存,git log将显示原始提交和正确的路径:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

如何使用:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

如果做一点更改,您甚至可以将合并的存储库的文件/目录移动到不同的路径,例如:

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

通知
路径通过 sed 替换,因此请确保合并后它移动到正确的路径中。
--allow-unlated-histories 参数仅自 git >= 2.9 起存在。

This function will clone remote repo into local repo dir, after merging all commits will be saved, git log will be show the original commits and proper paths:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

How to use:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

If make a little changes you can even move files/dirs of merged repo into different paths, for example:

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

Notices
Paths replaces via sed, so make sure it moved in proper paths after merging.
The --allow-unrelated-histories parameter only exists since git >= 2.9.

悲欢浪云 2024-08-18 00:43:23

基于本文,使用子树对我有用,并且只传输了适用的历史记录。如果有人需要这些步骤,请在此处发布(请确保将占位符替换为适用于您的值):

在源存储库中将子文件夹拆分为新分支

git subtree split --prefix=-b subtree-split-result

目标存储库中的合并拆分结果分支

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

验证您的更改并提交

git status
git commit

不要忘记

通过删除 subtree-split-result 分支进行清理

< code>gitbranch -D subtree-split-result

删除您添加的用于从源存储库获取数据的远程

git remote rm merge-source-repo

Based on this article, using subtree is what worked for me and only applicable history was transferred. Posting here in case anyone needs the steps (make sure to replace the placeholders with values applicable to you):

in your source repository split subfolder into a new branch

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

in your destination repo merge in the split result branch

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

verify your changes and commit

git status
git commit

Don't forget to

Clean up by deleting the subtree-split-result branch

git branch -D subtree-split-result

Remove the remote you added to fetch the data from source repo

git remote rm merge-source-repo

终难遇 2024-08-18 00:43:23

添加另一个答案,因为我认为这更简单一些。将 repo_dest 拉取到 repo_to_import 中,然后完成推送 --set-upstream url:repo_dest master 。

这种方法对我来说很有效,可以将几个较小的存储库导入到一个较大的存储库中。

如何导入:repo1_to_import 到 repo_dest 在

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

导入之前重命名或将文件和目录移动到原始存储库中的所需位置。例如,

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

以下链接中描述的方法启发了这个答案。我喜欢它,因为它看起来更简单。但要小心!那里有龙! https://help.github.com/articles/importing-an-external -git-repository git push --mirror url:repo_dest 将本地存储库历史记录和状态推送到远程 (url:repo_dest)。但它会删除遥控器的旧历史记录和状态。乐趣随之而来! :-E

Adding another answer as I think this is a bit simpler. A pull of repo_dest is done into repo_to_import and then a push --set-upstream url:repo_dest master is done.

This method has worked for me importing several smaller repos into a bigger one.

How to import: repo1_to_import to repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

Rename or move files and dirs into desired position in original repo before you do the import. e.g.

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

The method described at the following link inspired this answer. I liked it as it seemed more simple. BUT Beware! There be dragons! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_dest pushes your local repo history and state to remote (url:repo_dest). BUT it deletes the old history and state of the remote. Fun ensues! :-E

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文