继续重建git repo

时间:2017-12-23 19:19:03

标签: git

我想用一些代码重新格式化和其他代码过滤器来重放git repo ......是的,我知道这样做的所有风险。

不幸的是,这需要很长时间,不可能长时间冻结工作。我知道如何在某个时候重播分支。

我正在寻找的是如何从另一个回购重播分支并获得resume的想法。

基本上是伪代码中的算法:

starting_sha = very_last
if resume {
    starting_sha = last_applied_sha
}
for_each sha = commit --reversed from starting_sha to the HEAD {
    git checkout sha
    apply some changes to the code
    git commit to target repo with metadata from sha
    update last_applied_sha = sha
}

显然,我可以很容易地实现这样的脚本,但是git commit to target repo with metadata from sha是我希望我不需要自己处理的事情。

我希望有一些git filter-branch类型的功能可以让我这样做,而不需要自己处理标签和任何其他内部。

2 个答案:

答案 0 :(得分:2)

1。通过克隆源来设置目标存储库。

$ git clone <sourceRepo>

2。查看相关分支。将branchname替换为实际的分支名称(同样在以下所有步骤中)。

$ git checkout branchname

3。使用filter-branch--tree-filter进行初始重写,使用--tag-name-filter更新流程中的代码。这只是一个示例过滤器,它在匹配“* .txt”glob的所有文件中替换第一次出现的“text”和“modified”。

$ git filter-branch --tree-filter 'sed -i "s/text/modified/" *.txt' --tag-name-filter cat -- branchname

4。创建一个标记以保留最后一个源和目标转速的记录。

$ git tag lastsourcerev origin/branchname
$ git tag lasttargetrev branchname

现在,无论何时从源代码库更新到新修订版,都可以使用以下步骤。它们只将树过滤器应用于新的提交,并将新的(重写的)提交移植到现有的(以前重写的)提交。

1。从源代码中获取新的提交/标记:

$ git fetch origin

2。重置为源分支的新提示。

$ git reset --hard origin/branchname

3。使用额外filter-branch应用--parent-filter以将新提交移植到现有提交。请注意,我们需要-f(强制)选项作为前一个filter-branch命令refs/original--parent-filter使用存储最后源和目标转速的标签。整个filter-branch仅限于上次处理的源rev和最新的源提交(我们将branchname重置为)之间的提交。

$ git filter-branch -f --tree-filter 'sed -i "s/text/modified/" *.txt' --tag-name-filter cat --parent-filter "sed s/$(git rev-parse lastsourcerev)/$(git rev-parse lasttargetrev)/g" -- lastsourcerev..branchname

4。将跟踪代码更新为新情况:

$ git tag -f lastsourcerev origin/branchname
$ git tag -f lasttargetrev branchname

根据需要重复这些步骤。一旦不再进行更新,就可以删除lastsourcerevlasttargetrev帮助程序标记。

请注意,通过将分支重置为源之间的某些中间提交和提交为lastsourcerev的记录,可以将更新过程任意分割为更小的增量。同样,初始重写可以通过创建一个分支来分割,该分支指向来自源的中间提交并将其记录为lastsourcerev,然后应用更新步骤以进一步。

另请注意,此过程仅依赖于filter-branch,以避免任何有关标记重写或合并提交的问题,这些问题会导致重新设置新的传入提交,否则将不可避免地导致这些问题。

作为shell脚本打包,增量更新部分可能如下所示:

#!/bin/sh

REMOTE=origin
LOCAL_BRANCH=master
REMOTE_BRANCH=origin/master
SOURCE_REV_TAG=lastsourcerev
TARGET_REV_TAG=lasttargetrev
TREE_FILTER='sed -i "s/text/modified/" *.txt'

set -e

git fetch "$REMOTE"

if [ $(git rev-parse "$SOURCE_REV_TAG") = $(git rev-parse "$REMOTE_BRANCH") ]
then
    echo "no new commits, nothing to do"
    exit 0
fi

git checkout "$LOCAL_BRANCH"
git reset --hard "$REMOTE_BRANCH"

git filter-branch -f --tree-filter "$TREE_FILTER" \
    --tag-name-filter cat \
    --parent-filter "sed s/$(git rev-parse "$SOURCE_REV_TAG")/$(git rev-parse "$TARGET_REV_TAG")/g" \
    -- "$SOURCE_REV_TAG"..

git tag -f "$SOURCE_REV_TAG" "$REMOTE_BRANCH"
git tag -f "$TARGET_REV_TAG"

出现的唯一边缘情况是没有新的提交可用。在这种情况下,git reset --hard会将本地分支更新为远程分支,但是不会应用过滤步骤,因为不会重写转速。上面的脚本通过检查源rev跟踪标记是否指向与远程分支相同的提交来处理它。

答案 1 :(得分:0)

您可以应用git filter-branch而不是交互式rebase,它将访问您的repo的每次提交并应用您想要的任何实用程序(或代码重新格式化)。

由于filter-branch是一个本地操作,因此不需要另一个&#34;另一个&#34; repo:你把它应用到你的回购的本地克隆 请注意它does not support a pause/resume workflow,因此您需要让它处理完成。

参见&#34; Reformatting Your Codebase with git filter-branch&#34; (以 Elliot Chance )为例:

git filter-branch --tree-filter 'phpcbf $(\
  git show $GIT_COMMIT --name-status | egrep ^[AM] |\
    grep .php | cut -f2)' -- --all

对于每次提交,仅查找添加/修改的文件,隔离php文件并应用格式化工具。

这并不妨碍任何人在此期间提交 您的协作者需要克隆新的(格式化的)仓库,在新仓库的(新格式化的)分支历史记录之上添加他们自己的远程,获取和重新定位他们自己的提交(只有新的提交)。 /> 换句话说,每个协作者都要完成一个协调步骤,以便整合重组期间完成的工作。

如果没有,则需要反转该过程,并且您的新repo必须添加旧的repo(每个人都有推送,假设最近的提交格式正确)作为远程(名为&#39; {{1} }&#39;):

oldRepo
  • 您可以lists commits after a certain date(即格式化过程开始的日期)
  • 每次提交
  • (从最旧到最新),find its branchcd /path/to/new/repo git remote add oldRepo /path/to/old/central/repo git fetch oldRepo
  • 对于每个新分支,请执行git branch --contains

这将在分支上检测到旧提交的父级后重播所有提交&#39; git rebase --onto abranch acommit~ oldRepo/abranch&#39;到新的repo oldRepo/abranch(由于在重新编写新的repo时它们已完成并被推送,因此缺少提交)