从当前的HEAD创建一个新的Git存储库,将其设置为原始存储库的远程,并镜像两个存储库的将来更改?

时间:2018-12-09 05:29:39

标签: git git-rebase git-cherry-pick

当前存储库中已提交

  A -> B -> C
            ^
            |
         HEAD

我想创建一个新的存储库,其主分支始于当前存储库的提交C(HEAD)。

  C
  ^
  |
HEAD

此外,如果将新的提交D添加到当前存储库:

  A -> B -> C -> D
                 ^
                 |
                HEAD

新的存储库将变为:

  C -> D
       ^
       |
      HEAD

在下一个推送/镜像上。

由于在我对项目进行更改时学会了开发软件,因此由于在其悠久的历史(500次提交)中添加和删除了大文件,该存储库的大小有所增加。

可以在git中轻松实现此工作流程吗? (同时使用GitHub和GitLab)

3 个答案:

答案 0 :(得分:3)

您可以轻松地从现有仓库(至少在本地)创建一个新仓库,只需git clone <src repo> [dest dir](可以使用--depth或类似的方法来节省空间,尽管有一些注意事项,请参见{{3 }}了解详情。但是,使新的存储库自动遵循原始存储库的历史记录并不容易。新的存储库将具有其origin设置,以指向原始存储库,但更新将需要拉/取回+合并/照常进行。您可能可以在旧的仓库中设置一些提交后的钩子来自动执行cd <new repo> ; git pull ; cd $OLDPWD位,我对git钩子的工作原理并不了解。或者,您可以将新存储库设置为旧存储库中的一个远程服务器并推送到该存储库,尽管我不确定这将如何影响新存储库的工作树(即,检出的内容)。而且,与像GitHub这样的远程提供者一起进行的任何工作都将是完全不同的蠕虫病毒。

如果您想清理历史记录,则可能需要研究rebase,甚至可能要研​​究cherry-pick

答案 1 :(得分:2)

您试图做的几乎是不可能的。在git中,导致给定提交的历史记录是该提交不可分割的一部分。因此,在以下两个历史中用C表示的提交

  A -> B -> C
            ^
            |
         HEAD

  C
  ^
  |
HEAD

实际上是两个不同的提交对象,最有可能具有两个单独的哈希。实现所需设置的唯一方法是将这两个不同的提交对象调整为具有相同的哈希值,在这种情况下,您可以愚弄git将基于C的新提交推送到具有不同历史记录的不同存储库中。从理论上可以做到,但实际上却很难做到(如果您能够做到这一点,那么您也将能够破解数字签名的文档或更改比特币区块链)。

所需流量的近似值是在本地存储库中维护两个分支,分别对应于两个远程服务器。您将在其中一个分支上工作,并定期将其合并到另一个分支中:

old_repo_branch:      A -> B -> C ---->  D' -> E'
                                         ^     ^
                                        /     /
                                       /     /
new_repo_branch:                C' -> D --> E

您将不得不将new_repo_branch推送到新的存储库,并将old_repo_branch推送到旧的存储库。但是,如果您需要分支开发,那么这样的流程将变得难以管理(因为每个并行开发流都需要分支,并且每对相应的分支对也需要保持同步)。

答案 2 :(得分:2)

序言

您应该真正考虑您的工作流程。您很可能正在尝试实现从某些古老的VCS复制的奇怪工作流程。 Git用于跟踪历史记录,并让您重写它。但是,您需要确定所需的历史记录。对历史记录进行变体管理可能不是一个好主意。

500次提交对于Git来说并不是一个大数目, Linux内核大约有63.000 (!) commits just in 2018;)

解决方案

尽管如此,这还是一个可以满足您需求的概念性证明。不需要专用的存储库,重写的历史记录仅存储在某个专用的分支中。第一次运行将创建该孤立分支,随后的运行将使用最新的提交对其进行更新。两个呼叫看起来都一样:

$ path/to/crazy-rebase <rewritten-branch> <last-commit-to-transfer>

例如:

$ ./crazy-rebase cutoff master

工作原理

在第一次运行期间,脚本从给定的修订版本(例如cutoff)创建了一个孤立分支(例如master),而没有任何以前的历史记录。 所有进一步的运行都将挑选每个对该孤儿分支的提交(尚不存在)(使用重新设置)。从最后一次成功完成中推断出所需的提交(实际上,该提交存储在特殊引用CUTOFF_BASE中)。

脚本crazy-rebase

#!/usr/bin/env bash

CUTOFF="$1"
CURRENT="$2"

LAST_BASE="CUTOFF_BASE"


error() {
    local errcode=$?
    echo "ERR: $*" >&2
    exit $errcode
}

log() {
    echo "LOG: $*" >&2
}

ret() {
    return "$1"
}


prepare() {
    local cutoff="$1"
    local current="$2"
    local base_hash

    git show-ref --quiet "$cutoff" &&
    return 0

    log "Preparing cut-off branch '$cutoff' ..." &&
    base_hash="`git show -s --pretty=%H "$current"`" &&
    git checkout --quiet --orphan="$cutoff" "$current" &&
    git commit -m "Cutoff branch, based on '$base_hash'" &&
    git checkout --quiet "$current" &&
    git update-ref "$LAST_BASE" "$base_hash" &&
    log "Cut off branch '$cutoff' created." &&
    exit 0 ||
    error "Failed to init cut-off branch '$cutoff'."
}

rebase() {
    local cutoff="$1"
    local current="$2"
    local current_hash
    local errcode

    log "Rebasing commits '$LAST_BASE..$current' onto cut-off branch '$cutoff' ..."
    current_hash="`git show -s --pretty=%H "$current"`" &&
    git rebase --rebase-merges --onto "$cutoff" "$LAST_BASE" "$current_hash" || {
        errcode=$?
        log "STARTING INTERACTIVE SHELL TO RESOLVE REBASE."
        log "Use 'git rebase --continue' after resolving the issue e.g. with 'git mergetool'."
        log "Do not forget to exit this shell to continue the script."
        $SHELL
        if test -e "`git rev-parse --git-dir`/rebase-merge"; then
            git rebase --abort 2>/dev/null
            git checkout --quiet "$current"
            ret $errcode
            error "Failed to transfer commits '$LAST_BASE..$current' to '$cutoff'."
        fi
    } &&
    git rebase --rebase-merges HEAD "$cutoff" &&
    git checkout --quiet "$current" &&
    git update-ref "$LAST_BASE" "$current" &&
    log "Cut-off branch '$cutoff' updated." &&
    true
}


prepare "$CUTOFF" "$CURRENT" &&
rebase "$CUTOFF" "$CURRENT" &&
true

如果要将结果推送到远程存储库,请使用以下方法:

$ git push <remote> cutoff:<name-of-cutoff-on-remote>