如何将.gitignore添加到Git中存储库中的所有分支?

时间:2016-11-18 12:16:50

标签: git github

我的存储库中有超过5个分支。 我想将.gitignore文件添加到每个分支并将其推送到远程存储库。

我在克隆远程存储库后尝试逐个运行以下命令。

我按顺序运行的命令:

 for branch in $(git branch --all | grep '^\s*remotes' | egrep --invert-match '(:?HEAD|master)$'); do    git branch --track "${branch##*/}" "$branch"; done

for branch in $(git branch); do    git checkout $branch && touch .gitignore; done

for branch in $(git branch); do    git checkout $branch && git status; done

for branch in $(git branch); do    git checkout $branch && echo ".classpath" >> .gitignore && echo ".project" >> .gitignore && echo ".settings/" >> .gitignore && echo "log/" >> .gitignore && echo "target/" >> .gitignore ; done

for branch in $(git branch); do    git checkout $branch && git add .gitignore; done

for branch in $(git branch); do    git checkout $branch && git commit -m 'Adding gitignore'; done

for branch in $(git branch); do    git checkout $branch && git push -u origin $branch; done

不知道,但它没有按预期执行。

请帮忙。

2 个答案:

答案 0 :(得分:2)

首先,git branch是用户友好的,不是编程友好的。不要将它用于编程:使用编程友好的"管道"命令,git for-each-ref

其次,作为Leon said in a comment,编写脚本 - 一个一次性的脚本是一个好主意,如果需要,可以更轻松地编写和使用新的(新鲜,干净)克隆,直到你做对了 - 一个循环在循环中完成所有必要的工作。

最后,根本不要执行git push 。分开做。 Git有一种内置的方式来推动所有分支机构"。

您的第一个循环读取(使用我的格式):

 for branch in $(git branch --all | grep '^\s*remotes' |
         egrep --invert-match '(:?HEAD|master)$'); do 
     git branch --track "${branch##*/}" "$branch"
 done

这看起来您想要使用现有的远程跟踪分支origin/*或其他)并从中创建新的本地分支,并将新的本地分支设置为具有相应的远程跟踪分支作为其上游。你出于某种原因在这里排除master(我想我知道原因),你也排除了显示为:

的恼人的符号HEAD引用
  remotes/origin/HEAD -> origin/master

剩余的循环不会排除master(但也无法处理*打印的git branch,因此它们至少会产生一些错误消息。因此,您似乎也希望在master上执行此操作。这意味着你可能只是因为它已经存在而在第一个循环中跳过它,这在新的克隆中通常是真正的 1

所以,让我们写一些我们期望在新的git clone上运行的东西,即没有master以外的分支的东西。 (尽管如此,请再次参阅脚注1。)我们也会尝试修复其他错误。

有一个小问题:我们假设远程名称为origin,因此远程跟踪分支的名称以refs/remotes/origin/开头。这样可以更容易地遍历所有名称并剥离origin/前缀。然后我们开始编写脚本:

for name in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin); do

这将迭代所有缩短的名称:origin/HEADorigin/master,依此类推。

接下来,我们要跳过那些实际上是HEAD等符号引用的内容。我们可以跳过文字HEAD,这简单得多。奇特的方式是使用git symbolic-ref来测试它,但没有意义,我们将看到的唯一实际的(由于历史原因)是HEAD,所以让我们这样做:< / p>

    [ "$name" = origin/HEAD ] && continue

现在我们需要做的是创建并签出一个 local 分支,该分支将相应的远程跟踪分支设置为其上游。我们可以使用内置的git checkout在一个简单的步骤中完成此操作。

这也让我们避免使用特殊套管mastergit clone已经运行git checkout master,创建master并将其设置为origin/master作为其上游,但我们还是需要使用git checkout master。这将检查master创建的现有 git clonegit clone运行时git checkout master)。因此,我们的下一步是构建正确的分支名称:

    branch=${name#origin/}
    git checkout $branch

我们只想剥离origin/,而不是&#34;一切都是最后一个斜杠&#34;,以防其中一个远程跟踪分支命名为origin/feature/life而另一个名为{{例如{1}}。我们需要本地origin/feature/zorg,我们可以从中feature/life更新。

如果分支是origin/feature/life,它已经存在,并将其检出。如果分支是其他任何东西,它不存在,但由于这是一个新的克隆,只有一个远程跟踪分支 存在 - 具有master的那个 - 所以我们&#39; ll创建本地分支并将其检出,并将远程跟踪分支设置为其上游。

现在我们要创建一个$name,在其中放入一堆东西,添加它并提交它。一个重要的问题是我们刚签出的提交中是否已经存在.gitignore,如果存在,我们想要做些什么。我无法为你解答这个问题。另一个是我们刚要签出的提交中是否存在我们要添加这个.gitignore的任何文件,如果是,那么该怎么做。再一次,无法回答这个问题。不过,我会注意到.gitignore后跟touch .gitignore只会在git status新创建.gitignore时打印。直接测试可能更明智:

touch

如果&#34;应该做什么&#34;我没有什么特别的东西,我们可以简单地使用 if [ -f .gitignore ]; then ... do whatever should be done here ... else ... create it ... fi 添加它,如果它不存在则会创建它,或者如果它存在则添加它。

我们可以使用一个>>和一个&#34; here-document&#34;而不是五个单独的echo ... >> .gitignore

cat >> .gitignore

现在我们像往常一样 cat << END >> .gitignore .classpath .project .settings log/ target/ END git add,这就是我们循环的结束:

git commit

将所有这些写入shell脚本文件( git add .gitignore git commit -m 'add gitignore' done /tmp/doit.sh是我最喜欢的脚本名称)。在顶部添加/tmp/t.sh行或#! /bin/sh -e并使文件可执行 - set -e确保如果任何命令因任何原因失败,脚本将退出。 (也可以添加-e进行调试。)在循环之前添加一个显式set -x行,以便克隆存储库,这样我们就可以确保有一个新的克隆。请记住将目录更改为克隆。

本例的其余部分将新的repo放入名为&#34; newrepo&#34;的目录中。使用git clone制作临时目录可能更好。

mktemp -d

现在您可以运行#! /bin/sh -e # set -x # for debug [ -d newrepo ] && { echo "error: newrepo directory already exists"; exit 1; } git clone scheme://example.com/path/to/repo.git newrepo cd newrepo for name in ... # see above for the rest 并确保一切正常。如果是这样,继续;如果没有,请修复脚本并重新开始。

一切正常后,我们就会准备好/tmp/doit.sh

git push

最后一步是使用 refspec 进行推送,并且refsepec在这里说&#34;对于我的每个分支,使用同名 on origin&#34;。由于我们确保我们的分支具有相同的名称,例如$ cd newrepo $ git push origin 'refs/heads/*:refs/heads/*' - 与feature/life上的名称相同,这正是我们想要的。

(注意:以上都是未经测试的。请注意拼写错误等。)

答案 1 :(得分:0)

我已根据@torek建议解决,您可以按照以下步骤添加.gitignore文件:

#! /bin/sh -e
# set -x  # for debug
[ -d mydir] && { echo "error: mydir directory already exists"; exit 1; }
git clone <git-repo-url> mydir
cd mydir
for name in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin); do
     [ "$name" = origin/HEAD ] && continue
     branch=${name#origin/}
     git checkout $branch
     echo ".classpath" >> .gitignore && echo ".project" >> .gitignore && echo ".settings/" >> .gitignore && echo "log/" >> .gitignore && echo "target/" >> .gitignore
     git add .gitignore
     git commit -m 'adding gitignore'
done

完成上述操作后,使用以下命令推送代码:

git push origin 'refs/heads/*:refs/heads/*'