Как настроить драйвер git, чтобы игнорировать папку при слиянии - PullRequest
13 голосов
/ 24 июня 2010

Я уже провел обширный поиск, прочитал много SO вопросов и решений и попробовал его по-разному, но я не смог сделать то, что я хочу, что довольно просто.

У меня есть основная ветвь, в которой находится весь основной код, и ветвь дизайна, где команда разработчиков создает макет приложения rails. Они добавили папку с названием «photoshop» в общую папку, чтобы их источники изображений также находились под контролем версий. Но я не хочу, чтобы эта папка копировалась при слиянии с основной веткой, потому что, ну, она не нужна.

Видимо, способ сделать это через драйвер слияния. Итак, я создал драйвер "ignore":

[merge "ignore"]
name = always ignore during merge
driver = ignore.sh %0 %A %B

И создал файл ignore.sh на моем $ PATH:

exit 0

Я создал файл .gitattributes внутри public /, потому что папка photoshop должна полностью игнорироваться и она появится в public /:

photoshop  merge=ignore
photoshop/ merge=ignore
photoshop/* merge=ignore
photoshop/**/* merge=ignore

Как видите, я пробовал несколько разных шаблонов, чтобы игнорировать всю папку, но это не работает. Я полагаю, что это потому, что в главной ветви нет папки, поэтому нет конфликта, и поэтому git не использует драйвер игнорирования. Есть ли способ достичь этого, не создавая папку public / photoshop на master?

Спасибо!

Ответы [ 3 ]

18 голосов
/ 19 апреля 2012

Как следует из моего другого ответа, здесь приводится расширенная, обобщенная, промышленная версия решения.

(да, мне было скучно дома, и мне больше нечего было делать: P)

Этот скрипт добавит новый отдельный коммит, основанный на вашей локальной ветви разработки, поэтому он не повлияет ни на репозиторий проекта, ни на вашу ветку дизайна. В коммите будут удалены все нужные файлы. Затем он выполняет объединение.

Для тех, кому лень читать полный код, «ядро» этих шагов можно упростить как:

original=$(gitbranch HEAD)    # current branch name, or sha1 if not in a branch
branchsha=$(gitsha "$branch") # sha1 of a ref, to force detached commit

git checkout "$branchsha"   &&
git rm -rf "${files[@]}"    &&
git commit -m "$msgcommit"  &&
newsha=$(gitsha HEAD)       &&
git checkout "$original"    &&
git merge -m "$msgmerge" "${mergeopts[@]}" "$newsha"

А вот полный скрипт:

(немного изменен, чтобы справиться со слабой и ограниченной окраской синтаксиса SO, поэтому лучше получить первозданный источник по ссылке ниже)

* ** 1 022 тысяча двадцать-один * ГИТ-стрип-слияние
#!/bin/bash
#
# git-strip-merge - a git-merge that delete files on branch before merging
#
#    Copyright (C) 2012 Rodrigo Silva (MestreLion) <linux@rodrigosilva.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program. If not see <http://www.gnu.org/licenses/gpl.html>
#
# Answer for "How to setup a git driver to ignore a folder on merge?"
# See http://stackoverflow.com/questions/3111515

#Defaults:
msgcommit="remove files from '<branch>' before merge"
msgmerge="Merge stripped branch '<branch>'"
verbose=0
quiet=(--quiet)

usage() {
    cat <<- USAGE
    Usage: $myname [git-merge options] [-M <commitmsg>] <branch> FILE...
    USAGE
    if [[ "$1" ]] ; then
        cat >&2 <<- USAGE
        Try '$myname --help' for more information.
        USAGE
        exit 1
    fi
    cat <<-USAGE

    "git-merge that delete files on "foreign" <branch> before merging

    Useful for ignoring a folder in <branch> before merging it with
    current branch. Works by deleting FILE(S) in a detached commit based
    on <branch>, and then performing the merge of this new commit in the
    current branch. Note that <branch> is not changed by this procedure.
    Also note that <branch> may actually be any reference, like a tag,
    or a remote branch, or even a commit SHA.

    For more information, see <http://stackoverflow.com/questions/3111515>

    Options:
      -h, --help
         show this page.

      -v, --verbose
         do not use -q to supress normal output of internal steps from git
         checkout, rm, commit. By default, only git merge output is shown.
         Errors, however, are never supressed

      -M <message>, --msgcommit=<message>
         message for the removal commit in <branch>. Not to be confused
         with the message of the merge commit, which is set by -m. Default
         message is: "$msgcommit"

      -m <message>, --message=<message>
         message for the merge commit. Since we are not merging <branch>
         directly, but rather a detached commit based on it, we forge a
         message similar to git's default for a branch merge. Otherwise
         git would use in message the full and ugly SHA1 of our commit.
         Default message is: "$msgmerge"

      For both commit messages, the token "<branch>" is replaced for the
      actual <branch> name.

    Additional options are passed unchecked to git merge.

    All options must precede <branch> and FILE(s), except -h and --help
    that may appear anywhere on the command line.

    Example:
      $myname design "photoshop/*"

    Copyright (C) 2012 Rodrigo Silva (MestreLion) <linux@rodrigosilva.com>
    License: GPLv3 or later. See <http://www.gnu.org/licenses/gpl.html>"
    USAGE
    exit 0
}

# Helper functions
myname="${0##*/}"
argerr()  { printf "%s: %s\n" "${0##*/}" "${1:-error}" >&2 ; usage 1 ; }
invalid() { argerr "invalid option: $1" ; }
missing() { argerr "missing ${2:+$2 }operand${1:+ from $1}." ; }

# Option handling
files=()
mergeopts=()
for arg in "$@"; do case "$arg" in -h|--help) usage ;; esac; done
while (( $# )); do
    case "$1" in
    -v|--verbose  ) verbose=1            ;;
    -M            ) shift ; msgcommit=$1 ;;
    -m            ) shift ; msgmerge=$1  ;;
    --msgcommit=* ) msgcommit=${1#*=}    ;;
    --message=*   ) msgmerge=${1#*=}     ;;
    -*            ) mergeopts+=( "$1" )  ;;
    *             ) branch="$1"
                    shift ; break        ;;
    esac
    shift
done
files+=( "$@" )

# Argument handling

msgcommit=${msgcommit//<branch>/$branch}
msgmerge=${msgmerge//<branch>/$branch}

[[ "$msgcommit" ]]  || missing "msgcommit" "MSG"
[[ "$branch"   ]]   || missing ""          "<branch>"
(( ${#files[@]} ))  || missing ""          "FILE"

((verbose)) && quiet=()

# Here the fun begins...
gitsha()    { git rev-parse "$1" ; }
gitbranch() {
    git symbolic-ref "$1" 2> /dev/null | sed 's/refs\/heads\///' ||
    gitsha "$1"
}

original=$(gitbranch HEAD)
branchsha=$(gitsha "$branch")

trap 'git checkout --quiet "$original"' EXIT

git checkout "$branchsha"  "${quiet[@]}" &&
git rm -rf "${files[@]}"   "${quiet[@]}" &&
git commit -m "$msgcommit" "${quiet[@]}" &&
newsha=$(gitsha HEAD)                    &&
git checkout "$original"   "${quiet[@]}" &&
git merge -m "$msgmerge" "${mergeopts[@]}" "$newsha"

Наслаждайтесь!

Изображение стоит больше тысячи слов ...

До слияния:

enter image description here

После слияния:

enter image description here

Обратите внимание, что подсказка "design" ветви не была затронута вообще, даже будучи локальной ветвью, благодаря трюку с отсоединенным коммитом. Кроме этого, обе фиксации (удаление и слияние) являются регулярными фиксациями с подходящими сообщениями фиксации и родителями. А ветка "master" очищена от любых нежелательных файлов.

10 голосов
/ 10 марта 2012

Не ответ как таковой , но несколько замечаний о .gitignore: не поможет вам в вашем сценарии.

.gitignore предназначен для игнорирования файлов из рабочего дерева , которые будут добавлены к index (область подготовки). Таким образом, он эффективен только при использовании git add <files>, и его можно переопределить, используя git add --force <files>. Это просто удобство для предотвращения добавления нежелательных файлов, но оно не влияет на файлы внутри хранилища

В вашем сценарии .gitignore не требуется, поскольку у вас нет локальной папки ./photoshop, поэтому у вас никогда не будет файлов Photoshop для добавления в основную ветку. Однако не мешало бы создать его на всякий случай. И для команды разработчиков .gitignore не приветствуется, поскольку они хотят, чтобы файлы фотошопа были добавлены в их ветку.

Итак, поскольку слияния имеют дело с подтвержденными данными , а файлы ./photoshop уже находятся внутри хранилища, ваш подход к использованию драйвера слияния был правильным.

Проблема в том, что ... по умолчанию драйвер слияния запускается только в случае конфликтов . И, поскольку ветка master не имеет папки или файлов ./photoshop, нет никаких конфликтов вообще , они полностью объединены. Таким образом, ваш драйвер слияния также не имел никакого эффекта, независимо от ваших шаблонов пути (кстати, ваш второй, photoshop/, был правильным). Я не знаю, можно ли настроить git merge на запуск драйвера слияния даже для неконфликтующих файлов, но стоит поискать.

Как я уже говорил, мой ответ не является истинным решением вашей проблемы. Я просто надеялся пролить свет на эту тему, объясняя, почему ваши попытки использовать драйвер слияния и .gitignore потерпели неудачу. Я предлагаю прочитать больше о (настройке) драйверов слияния. Также стоит изучить подмодули .

Надеюсь, это поможет!

UPDATE

Может быть, мой другой ответ поможет вам :

Usage: git-strip-merge [git-merge options] [-M <commitmsg>] <branch> FILE...

ГИТ-стрип-слияния

Наслаждайтесь!

0 голосов
/ 24 июня 2010

Вы пытались добавить файл .gitignore в ветку master, чтобы игнорировать содержимое каталога photopshop?
Тогда слияние с дизайна на master не должно добавлять этот новый каталог в master.

Если это работает, вам все еще нужен драйвер слияния, но на этот раз для управления содержимым файла .gitignore.

...