Git smudge/clean filter между ветвями

Есть много связанных вопросов, связанных с фильтрами smudge/clean - я потратил несколько часов на их чтение и пробовал различные варианты, но все еще не смог. Надеюсь, я могу спросить, каким образом я получаю ответ, который работает для меня.

В частности, я прочитал эту страницу на большинстве этих ответов, ссылаясь на:


TL;DR

Детальный вопрос, но резюме:

  • Можно ли хранить DEBUG = false в файле в одной ветке и DEBUG = true в другой ветке, используя фильтры smudge/clean для управления этим файлом? И как?

Фон

У меня есть разные удаленные репозитории, размещенные на битбакете. Я использую SourceTree на Win8, чтобы клонировать удаленные репозитории к моему ноутбуку. Я создаю разные ветки для разработки, функций, выпусков и т.д. (После успешная Git ветвящаяся модель для улучшения или хуже).

У меня есть класс Java java под названием Dbug.java, который содержит логическое значение, которое включает/выключает различные протоколы отладки, mocking и т.д. в моем коде.

public static final boolean DEBUG = false;

Я хотел бы, чтобы это значение было false в моей ветки "production" (master) и было true в моих ветвях функций.

  • Возможно ли использование фильтров, или я уже неправильно понял прецедент?
  • Я не уверен, что фильтры работают так, как между двумя ветвями одного локально размещенного репо, или если фильтры работают только между двумя репозиториями.

Создание фильтров

Работая локально, я проверил производственную ветвь. Я создал тестовый файл с именем debug_flag.txt со следующим содержимым:

// false on production branch
// true on other branches
DEBUG = false;

Я создал файл в корне моего локального репо под названием .gitattributes и добавил ссылку на фильтр:

debug_flag.txt filter=debug_on_off

Я обновил файл .git/config с помощью определения фильтра:

[filter "debug_on_off"]
    clean = sed -e 's/DEBUG = true/DEBUG = false/'
    smudge = sed -s 's/DEBUG = false/DEBUG = true/'
  • В моем понимании это должно гарантировать, что у моего файла всегда есть ложное значение в производстве, но будет иметь истинное значение, когда я перейду от производство.
  • Правильно ли это?

Тестирование фильтров

Я создал новую ветвь test, используя:

git checkout -b test

Я проверил содержимое моего файла:

$ cat debug_flag.txt

// false on production branch
// true on other branches
DEBUG = false;
  • Я ожидал увидеть значение true в файле
  • Не должен ли фильтр "smudge" запускаться, когда я проверил файл?

Я добавил новую строку в файл и зафиксировал ее. Затем я переключился обратно в производственную ветвь, и здесь ситуация становится странной.

Если я смотрю на файл в SourceTree, никаких изменений в этой ветке с момента его создания нет. Этого я и ожидал бы, поскольку единственное изменение было сделано в другой ветке.

Если я смотрю на файл в терминале или Notepad ++, я вижу, что мое значение изменилось:

$ cat debug_flag.txt

// false on production branch
// true on other branches
DEBUG = true;

Я еще не объединил изменение между тестовой ветвью, я не сделал фиксацию на производственной ветке, но изменился файл.

  • похоже, что фильтр smudge был запущен в файле внутри этой ветки, но не через ветки.

Мне не хватает жизненно важной части головоломки, и, надеюсь, это нечто простое, что может быть замечено кем-то, у кого есть опыт.

Моя ставка заключается в простом непонимании концепции.

Запрос Pls для любой отсутствующей информации...


Обновление на основе ответа VonC

Настройка базовых фильтров работала достаточно хорошо. Определили фильтр в файле config следующим образом:

[filter "debug_on_off"]
    clean = sed -e 's/DEBUG = true/DEBUG = false/'
    smudge = sed -s 's/DEBUG = false/DEBUG = true/'

Создание новой ветки исправляет false → true, слияние обратно меняет true → false.

Завершение изменения только для производственной (основной) ветки требовало специальных скриптов, которые знали о ветке, из которой они выполняются. Таким образом, файл config стал:

[filter "debug_on_off"]
    clean = ./scripts/master_clean.sh
    smudge = ./scripts/master_smudge.sh

master_clean.sh:

#!/bin/sh
branch=$(git rev-parse --symbolic --abbrev-ref HEAD)
if [ "master" = "$branch" ]; then
    sed -e s/DEBUG = true/DEBUG = false/ $1
else
    cat $1
fi

master_smudge.sh:

#!/bin/sh
branch=$(git rev-parse --symbolic --abbrev-ref HEAD)
if [ "master" = "$branch" ]; then
    sed -e s/DEBUG = false/DEBUG = true/ $1
else
    cat $1
fi

В этот момент я сталкиваюсь с несоответствиями между тем, что видит SourceTree, и тем, что показано в Notepad ++ для содержимого файла отладки. SourceTree показывает изменения, но Notepad ++ - нет.

Я принимаю ответ VonC, так как он отвечает на основной вопрос, который я задал.

Однако я, скорее всего, буду реализовывать решение которое я написал, поскольку он решает основную проблему, которую я пытаюсь решить, более простым способом (для меня): сохранение другого конфигурационного файла в отдельных ветвях.

Ответ 1

Я ожидал увидеть значение true в файле

Вы только что создали новую ветку, не проверили ее содержимое (ее содержимое совпадает с той ветвью, в которой вы были)

Чтобы заставить смазку запустить, сделайте в верхней части репо:

git checkout HEAD --

Я еще не объединил это изменение с тестовой ветвью, я не сделал фиксацию в производственной ветки, но файл изменился.

Это идея драйвера фильтра содержимого: он изменяет контент, не затрагивая git status (который все еще сообщает об измененном файле как "неизмененном" ).

Чтобы иметь размытие, действующее по-разному на каждую ветвь, я бы рекомендовал вызвать script, который начинается с поиска имени текущей ветки.
См. Пример в моем более раннем ответе "Лучшая практика - Git + Автоматизация сборки - Сохранение конфигураций отдельно.

#!/bin/sh
branch=$(git rev-parse --symbolic --abbrev-ref HEAD)

Ответ 2

Взгляните на expandr. Это script, который позволяет настраивать различные конфигурации на разных ветвях, используя smudge/clean. В основном именно то, что вы изначально запрашивали.

В настоящий момент самая большая проблема заключается в том, что после переключения ветвей иногда мне нужно сделать git checkout HEAD -- "$(git rev-parse --show-toplevel)", чтобы рабочий каталог был правильно смазан. В других случаях, однако, это работает нормально; Я еще не понял, почему. Я думаю, что это может быть связано с включением "merge renormalize", что вызывает некоторые проблемы? Я не уверен. (У меня оно есть.)

Другой способ заключается в том, что вы должны защищать каждую ветвь .gitattributes файлы сами с merge=ours, помещая строку, в которой говорится .gitattributes merge=ours, и, конечно же, включить драйвер для этого (как вы уже упоминали). gotcha заключается в том, что после создания каждой отдельной ветки теперь вы должны войти в каждый файл .gitattributes и изменить каждый из них (теперь я рекомендую добавить комментарий вроде #touched for merge=ours on master, #touched for merge=ours on test branch и т.д., так что вы помните, почему он был там). Вы должны сделать это, потому что merge=ours будет защищать файл только от изменения в версии входящей ветки в слиянии, если этот файл был изменен после создания ветки как в входящей ветки, так и в ее родительской ветке. (Помните, что git имеет дело с изменениями, а не с файлами.)

Ответ 3

Совет VonC описывает точный вопрос, который я задал, но мне не удалось определить окончательные детали (согласно моему обновлению на вопрос). Этот ответ дает подробные сведения о том, как я это сделал.


Update

Ниже метод работал для первого слияния. Но после этого он больше не работает. Я оставляю его здесь, так как он представляет текущее состояние моего расследования.

Кажется, что драйверы слияния больше не вызываются.

Также попробовали различные модификации связанных вопросов с помощью exit 0, touch %A или пользовательского script слияния (fooobar.com/questions/6109/...) вместо true, как показано ниже.


Я нашел обходной путь к этому, который использует настраиваемую стратегию merge для решения основной проблемы, которая:

  • Я хочу, чтобы файлы сборки в моей ветке сборки всегда устанавливались, чтобы отключить все значения отладки.
  • Это предотвращает случайные выпуски продукта с настройками макета, настройками локального хоста, ведением журнала и т.д.

Я основываюсь на информации по этому вопросу: .gitattributes и индивидуальная стратегия слияния для файла

1) Определите пользовательский драйвер слияния в файле .git/config следующим образом:

[merge "ours"]
    name = "Keep ours merge"
    driver = true

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

(для деталей: fooobar.com/questions/9401/...)

2) Настройте файл .gitattributes в ветке build/production/pristine, чтобы специальный флаг отладки использовал вышеупомянутую стратегию слияния.

Поэтому, используя файлы в моем вопросе, перейдите в ветвь "production" и добавьте следующую строку в файл .gitattributes:

debug_flag.txt merge=ours

Каждый раз, когда слияние возвращается в ветвь "production", git будет искать стратегию слияния, определенную как "наш", и будет препятствовать перезаписыванию debug_flag.txt.

3) На других ветвях настройте свой .gitattributes файл без этой стратегии слияния.

4) Последний (но важный) шаг процесса конфигурации - правильно настроить файл debug_flag.txt во всех ветвях и фиксировать изменения в каждой ветке.

Теперь у вас должно быть 2 ветки, каждая из которых содержит разные версии файлов .gitattributes и debug_flag.txt. Это гарантирует, что при каждом слиянии возникают конфликты.

Без конфликтов обычная стратегия слияния "наш" не вызывается, и файлы могут быть перезаписаны.

(для деталей: fooobar.com/questions/9401/...)

5) Наконец, объедините свою новую ветку в "производство". У вас возникнут конфликты слияния из-за шагов 3 и 4. Разрешите конфликты, чтобы 2 ветки сохраняли свои различия. Зафиксируйте изменения.

Все будущие слияния этих двух ветвей без проблем будут игнорировать файл debug_flag.txt.


Это позволяет создать 2 разных файла конфигурации на разных ветвях, чтобы вы могли легко отделить отладку от производственного кода и т.д. Это, похоже, является распространенным вариантом использования, со многими связанными с ним вопросами на этом форуме, но он по-прежнему мне пару дней, чтобы понять это.

Ответ 4

Самое прямолинейное решение - обновить ваш make файл, чтобы включить проверку того, какая ветка в данный момент проверена. Если ветвь является частью именованного списка, определите новый аргумент build -DDEBUG = true или -DDEBUG = false в противном случае.

См. Как программно определить текущую вывешенную ветвь Git

branch_name=$(git symbolic-ref -q HEAD)
branch_name=${branch_name##refs/heads/}
branch_name=${branch_name:-HEAD}

PROD_BRANCHES := master \
                QA
debug_flag=
ifneq ($(filter $(branch_name),$(PROD_BRANCHES)),)
    debug_flag="-DDEBUG=true"
endif
ifeq($debug_flag,)
    debug_flag="-DDEBUG=false"
endif