Git - 如何自动将目录中的更改推送到另一个分支

7

问题完整重写

我以为我非常简单明了地解释了这个问题,但似乎我过于简化了,所以在这里提供所有的额外细节。希望这可以帮助每个人看到这也不是一个重复的问题。


我有一个仓库(项目),我想自动化从一个分支中的一个目录推送提交到另一个分支的过程;这是我在SO上还没有遇到过的事情。

这是我的项目及其完整结构:

[PROJECT MASTER BRANCH]
|- gh-pages    (directory)
|- css         (directory)
|- index.html  (file)
|- readme.md   (file)

[PROJECT gh-pages BRANCH]
|- (empty at the moment)

我希望创建一个钩子,能够自动处理从主分支到我的 gh-pages 目录的更改,并将它们(无论是复制、克隆还是替换)到我的 gh-pages 分支,即项目网站分支。这里有一个例子,省略了所有其他文件:
[PROJECT MASTER BRANCH]
|- gh-pages         (directory)   <=== SEE UPDATE BELOW [A]
|  |- css           (directory)
|  |  |- style.css  (file)
|  |- index.html    (file)

[PROJECT gh-pages BRANCH]
|- css           (directory)   <=== SEE UPDATE BELOW [B]
|  |- style.css  (file)
|- index.html    (file)

我完全是新手级别的 Git Hub 用户,通常只使用基础功能,从不使用终端/ shell。为了澄清我的意图,我希望做到以下几点:
  • 只需要在 [Master Branch] 中工作。所有需要对 [gh-pages Branch] 做出的更改,都在 [Master Branch] 的 gh-pages 目录中完成。
  • 最好通过添加一个简单的文件来实现这个目标,似乎这个文件应该是 post-receive hook?
下面是我尝试使用的一些 post-receive hook 代码(通过学习一些内容制作),但它没有起作用:
#!/bin/bash
while read oldrev newrev refname
do
    branch=$(git rev-parse --symbolic --abbrev-ref $refname)
    if [ "master" == "$branch" ]; then
        git checkout gh-pages
        git checkout master -- gh-pages
        git add gh-pages
        git commit -m "Updating project website from 'master' branch."
    fi
done

注意
正如我在评论中提到的那样:这不是重复问题。这不是在问如何推送,而是如何添加其他命令,当我进行普通推送时自动运行这些命令。这些命令将执行我在原始帖子中提到的额外工作。

更新
我已经在下面引用了我的代码,并添加了箭头: <===

[A] 这里应该发生的是Git应该递归读取主分支gh-pages目录,并将更新的内容(或如果更容易则全部内容)复制到gh-pages分支中。

[B] 因此,如果主分支的gh-pages目录中有一个index.html文件和一个css文件夹,其中包含一个style.css文件,则只应复制该结构,而不是gh-pages目录本身。下面是一个错误的钩子示例,它也复制了gh-pages目录:

[PROJECT gh-pages BRANCH]
|- gh-pages         (Directory)   <=== NOT SUPPOSED TO HAPPEN
|  |- css           (directory)
|  |  |- style.css  (file)
|  |- index.html    (file)

此外,钩子不应复制除gh-pages内部文件以外的任何其他文件。即使主分支中有多个其他文件更改,也只应复制gh-pages目录文件。 [C] 新代码 - 这段代码可以工作,但会导致错误:
#!/bin/bash
branch=$(git rev-parse --abbrev-ref HEAD)
if [ "master" == "$branch" ]; then
    git fetch && git checkout gh-pages
    git checkout master -- gh-pages/*
    git add -A
    git commit -m "Updating project website from 'master' branch."
    git push -u origin gh-pages
fi

这种方法有两个问题。1)如果存储库滞后于提交,它无法处理,会出现错误;如果使用pull而不是fetch,则本地存储库被清除如下:

在将代码更改为pull时已清除本地存储库。

如果我保留fetch,则本地存储库保持不变:

推送后应该保留的本地存储库。此处使用了fetch。

2)整个gh-pages目录仍然被复制到gh-pages分支中,而不仅仅是其中的文件。

@gzh 这不是重复的问题。这不是在询问如何推送,而是如何添加其他命令以在正常推送时自动运行。这些命令将执行我在原始帖子中提到的额外工作。那个问题是关于每次用户提交时自动化整个Git周期,而不是提交到主分支并同时克隆一个目录到完全不同的分支。 - Blizzardengle
1
似乎您需要编写一个post-receive hook来完成这个任务。 - gzh
@gzh 提醒一下。问题已经重新表述了。Post-receive钩子是有意义的,但我不知道如何实现它。 - Blizzardengle
钩子脚本应该位于$your_repo/hooks/post-receive目录下,那么您的钩子脚本运行目录在哪里?您的检出代码将放在哪里?我认为您应该创建一个新文件夹,克隆源代码并进行所需操作。 - gzh
现在对我来说写一个工作示例不太方便,但是这里有一些在Windows上运行的钩子示例。祝你好运。 - gzh
显示剩余2条评论
2个回答

5
你真的不需要这种复杂的方法。只需将自己的repo作为子模块添加(添加到它本身),子模块跟随gh-pages分支(因为子模块可以跟随分支的最新提交)即可!
git checkout master
git rm -r gh-pages # save your data first
git submodule add -b gh-pages -- /remote/url/of/your/own/repo
git commit -m "ADd gh-pages branch as submodule"
git push

那样你就可以在主分支或实际上是子模块的gh-pages文件夹中更改文件。每当您在gh-pages文件夹中进行更改时,请不要忘记在那里提交,并在您的存储库的主文件夹中提交,以记录代表gh-pages子模块的新SHA1的新gitlinkindex中的特殊条目)。
cd myrepo/gh-pages
# modify files
git add .
git commit -m "modify gh-pages files"
cd .. # back to myrepo folder
git add gh-pages
git commit -m "record gh-pages new SHA1"
git push

使用 git 2.7+, 可以设置:

cd /path/to/main/repo
git config push.recurseSubmodules on-demand

然后,从主仓库进行一次git push操作也会将gh-pages子模块推送到gh-pages分支中。
稍后,一次git clone操作将克隆你的主仓库和它的gh-pages分支(在gh-pages子模块文件夹中)。
这样,你始终可以看到两者的内容。
而且,你不需要复杂的“同步/复制”机制。

2016年8月更新:现在更简单的GitHub Pages发布允许将页面文件保存在同一分支的子文件夹中(不再需要gh-pages):

Now you can select a source in your repository settings and GitHub Pages will look for your content there.

您不再需要子模块方法,因为您的分页器可以在同一分支的子文件夹中。


你让我有点懵:“每当你在gh-pages文件夹中进行更改时,不要忘记提交”。如果我在shell中运行你的代码,它可以工作,但是如果我尝试使用Git for Windows(桌面版)进行任何类型的正常提交,我会收到一个错误提示,要求使用shell添加子模型或删除.git文件。 - Blizzardengle
我尝试了这个答案,但没有成功:https://dev59.com/1m035IYBdhLWcg3wPNZk - Blizzardengle
我整晚都在尝试修改这个程序,只有两件事要说。1)它能用,谢谢。2)它没有回答我的问题,我要求一个非shell解决方案。我的目标和燃烧的愿望是每次使用Git的窗口GUI而不是shell。我制作了一个pre-commit钩子甚至是一个pre-push钩子,在我的GUI提交之前自动提交子模块更改,但没有成功。但是如果我在shell中手动执行,它就可以工作……虽然不太满意,但它确实可以工作。 - Blizzardengle
它将与任何使用最新的2.7.1 git的Windows GUI一起工作。当GitHub Desktop使用足够新的git嵌入其中时,它也将与之兼容(正如我所知道的那样,您无法更改其git版本:https://dev59.com/eJDea4cB1Zd3GeqPi_Qh#33703716)。拥抱命令行:您可以对其进行脚本编写,并使整个过程变得“一键式”。 - VonC
@Blizzardengle 如果你有一个调用最新的git(如Tortoise Git,SmartTree等)的GUI,则你只需要使用初始的git submodule addgit config push.recurseSubmodules on-demand命令行。其他所有操作都可以通过GUI完成。但不能使用GitHub Desktop。 - VonC
显示剩余3条评论

3
你写的钩子脚本有什么问题?在创建gh-pages分支时,它的内容是什么?
我使用以下命令创建了一个空的gh-pages分支:
git checkout --orphan gh-pages
git rm -rf .
touch README.txt
git add README.txt
git commit -m "Initial commit"
git push -u origin gh-pages

然后我作为post-receive hook的一部分运行了以下脚本,它对我有效。

#!/bin/bash
branch=`git rev-parse --abbrev-ref HEAD` 
if [ "master" == "$branch" ]; then
    git fetch && git checkout gh-pages
    git checkout master -- gh-pages
    git add -A
    git commit -m "Updating project website from 'master' branch."
    git push -u origin gh-pages
fi
done

我不理解你的代码,所以我认为你可能误解了。由于某种原因,我的钩子无法运行,但如果我在shell中自己运行所有内容,它似乎可以工作。它所做的只是将我整个主分支中的每个更改文件复制到gh-pages中,这不是我需要的。https://github.com/blizzardengle/barebones-framework - Blizzardengle
我编辑了我的问题,指出了不起作用的部分。 - Blizzardengle
这个从来没有按原样工作,因为branch=git rev-parse --abbrev-ref HEAD需要变成$(git rev-parse --abbrev-ref HEAD)。它还复制了更多的东西,即gh-pages目录。这是一个快速解决方案,可以考虑作为答案,但@VonC的答案更加准确。 - Blizzardengle
我们会像我昨天建议的那样制作一个shell来处理预期的任务。然而,我同意VonC的答案是值得的。 - SnehalK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接