Может ли git игнорировать определенную строку?

116

Я использую git для синхронизации с телефонным разговором во время тестирования в собственном браузере телефона. Таким образом, у меня есть следующая строка:

var isPhoneGap = false;

Очевидно, я меняю это при сборке, но есть ли способ настроить git, чтобы игнорировать эту единственную строку, или мне нужно пойти и поместить ее в отдельный файл и игнорировать ее таким образом?

Я использую Gitx и терминал на OSX 10.6.

Иоло
источник
@ user494461: фильтры Git - это способ сделать это: stackoverflow.com/a/16244970/520162
eckes 03
1
Разве вы не можете обнаружить среду и использовать файл конфигурации eniroment?
exussum
Вкратце: нет. Но я написал сценарий препроцессора, который ищет определенный закомментированный код и удаляет его перед публикацией в git. Проверьте это здесь: github.com/franklinchou/ahk_config/blob/master/preprocess.sh
franklin

Ответы:

104

Если ваш файл относится к определенному типу, вы можете объявить драйвер фильтра содержимого , который вы можете объявить в .gitattributesфайле (как показано в «Расширении ключевых слов» в « Атрибутах Git »):

http://git-scm.com/figures/18333fig0702-tn.png

*.yourType filter=yourFilterName

(вы даже можете установить этот фильтр для определенного файла , если хотите)

Воплощать в жизнь:

  • yourFilterName.smudge(срабатывает git checkout) и

    git config --global filter.yourFilterName.smudge 'sed "s/isPhoneGap = .*/isPhoneGap = true/"'
    
  • yourFilterName.clean(срабатывает git add)

    git config --global filter.yourFilterName.clean 'sed "s/isPhoneGap = .*/isPhoneGap = false/"'
    

Ваш файл будет отображаться без изменений git status, но его извлеченная версия будет иметь правильное значение для isPhoneGap.

VonC
источник
1
Спасибо за это. получил это работает. Вы знаете, как сделать git diff or git status`, игнорируя фильтры? Так что я все еще вижу, что изменилось? Мой вариант использования - журналы отладки ... которые в конечном итоге я хочу удалить ... @jthill @VonC
timh
1
@timh, если фильтр работает, git status или git diff ничего не должны показывать.
VonC
1
жаль, что в GIT нет более простого способа. git ignore <filename> <linenumber> будет очень кстати!
kiedysktos
22
@kiedysktos льняной номер? Что делать, если номер строки изменится?
VonC
Это отлично сработало, но будьте осторожны, пользователи Windows, gitconfig очень разборчив с тем, какие символы он хочет содержать, поэтому при попытке использовать некоторые полуэкзотические регулярные выражения вы можете столкнуться с проблемами. В итоге я поместил свои вызовы sed в отдельный файл helper.sh, который я вызвал из своего gitconfig, sh ".git/helper.sh"убедившись, что пропущены все параметры для sed "$@"(я предполагаю, что передан только путь к файлу ).
ohaal
43

Ты можешь использовать

git update-index --assume-unchanged [file]

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

Когда в файле есть важные изменения, вам необходимо сделать следующее:

git update-index --no-assume-unchanged [file]

Также см. Git doc update-index для --[no-]assume-unchangedпараметра.

Когда эти флаги указаны, имена объектов, записанные для путей, не обновляются. Вместо этого эти параметры устанавливают и сбрасывают бит «считать неизменным» для путей. Когда бит «предполагать неизменность» включен, git перестает проверять файлы рабочего дерева на предмет возможных модификаций, поэтому вам нужно вручную отключить бит, чтобы сообщить git, когда вы изменяете файл рабочего дерева.

Carlos
источник
2
Будет ли это по-прежнему получать новую версию, когда я выполняю git pull?
Саад Рехман Шах
1
@Caffeine, когда вы выполняете git pull, вы получаете последнюю версию, которая была зафиксирована, это будет последняя версия перед тем, как вы измените файл на '--assume-unchanged'.
Карлос
1
Я считаю, --skip-worktreeчто это лучший вариант для большинства целей. stackoverflow.com/a/39583010/4233593
Джефф Пакетт
3
это игнорирует обновления всего файла, а не отдельной строки
nikoss 08
6

Gitx должен позволять вам фиксировать или игнорировать отдельные строки (вы, возможно, уже знаете это), но вам придется делать это каждый раз, когда вы фиксируете. Я думаю, что было бы лучше иметь файл конфигурации для каждой цели развертывания (вы можете версия для них) и некоторый параметр времени выполнения, независимо от того, запускаете ли вы сервер (например ./myserver --config=whatever.js).

Энди Рэй
источник
5

Продолжая https://stackoverflow.com/a/20574486/4935114 , @Mike предложил создать pre-commitперехватчик, который будет grepв поэтапных файлах для строк, которые можно игнорировать. Хук проверяет, были ли эти строки поставлены. Если да, то это echoпредупреждение, и это exitсвязано с кодом, 1поэтому процесс фиксации не будет продолжен.

Вдохновленный ответом @Mike , я обнаружил, что использую, возможно, улучшенную версию его крючка, который автоматически reset-pфлагом) определяет конкретную строку, которую мы хотим игнорировать.

Я не уверен, что этот хук будет работать в ситуации, когда у вас есть много файлов с этой строкой, которые нужно игнорировать, но этот pre-commitхук ищет изменения в этой строке в конкретном файле buildVars.java. Скрипт перехвата выглядел так, когда я тестировал его на своей машине.

#!/bin/sh

# this hook looks for lines with the text `var isPhoneGap = false;` in the file `buildVars.java` and it resets these lines to the previous state before staged with `reset -p`

if [[ $(git diff --no-ext-diff --cached buildVars.java | grep --count -e "var\ isPhoneGap[\ ]*=[\ ]*") -ne 0 ]]; then
    cat <<EOW
WARNING: You are attempting to commit changes which are not supposed to be commited according to this \`pre-commit\` hook
This \`pre-commit\` hook will reset all the files containing this line to it's previous state in the last commit.
EOW
    echo /$'\n'isPhoneGap$'\n'y$'\n'q | git reset -p
    # BONUS: Check if after reseting, there is no actual changes to be commited and if so, exit 1 so the commit process will abort.
    if [[ $(git diff --no-ext-diff --cached | wc -l) -eq 0 ]]; then
        echo there are no actual changes to be commited and besides the change to the variable \'isPhoneGap\' so I won\'t commit.
        exit 1
    fi
fi

объяснение

Что я сделал, так это повторил контрольную последовательность, которая ищет регулярное выражение isPhoneGapво время интерактивного resetпроцесса. Таким образом, имитируя пользователя, который нажимает /для поиска isPhoneGap, нажимает, yкогда его спрашивают, хочет ли он отказаться от этого патча, и, наконец, нажимает, qчтобы выйти из интерактивного режима reset.

Интерактивный обратный процесс исправления описан здесь: https://git-scm.com/docs/git-add#git-add-patch


ПРИМЕЧАНИЕ. В приведенном выше сценарии предполагается, что переменная interactive.singleKey- false. Если вы настроили свой на true, удалите любой $'\n'из echoкоманды сразу после предупреждения.

Дорон Бехар
источник
3

Вот как это можно сделать с помощью фильтров git :

  1. Создать / открыть файл gitattributes:
    • <корень проекта> /. gitattributes (будет зафиксирован в репо)
      ИЛИ
    • <корень проекта> /. git / info / attributes (не будет зафиксирован в репо)
  2. Добавьте строку, определяющую файлы для фильтрации:
    • *.rb filter=gitignore, т.е. запустить фильтр gitignoreпо всем *.rbфайлам
  3. Определите gitignoreфильтр в своем gitconfig:
    • $ git config --global filter.gitignore.clean "sed '/#gitignore$/'d", т.е. удалить эти строки
    • $ git config --global filter.gitignore.smudge cat, т.е. ничего не делать при извлечении файла из репо

Примечания:
Конечно, это для файлов ruby, применяется, когда строка заканчивается на #gitignore, применяется глобально в ~/.gitconfig. Измените это, как вам нужно для ваших целей.

Предупреждение!!
Это оставляет ваш рабочий файл отличным от репо (конечно). Любая проверка или перестановка будут означать, что эти строки будут потеряны! Этот трюк может показаться бесполезным, поскольку эти строки постоянно теряются при извлечении, перемещении или извлечении, но у меня есть конкретный вариант использования, чтобы его использовать.

Просто git stash save "proj1-debug" пока фильтр неактивен (просто отключите его на время gitconfigили что-то в этом роде). Таким образом, мой код отладки всегда может быть git stash applyдобавлен к моему коду в любое время, не опасаясь, что эти строки когда-либо будут случайно зафиксированы.

У меня есть возможная идея решения этих проблем, но я попробую реализовать ее в другой раз.

Спасибо Руди и jw013 за упоминание фильтров и атрибутов git.

каче
источник
2

Драйвер контентного фильтра - не лучшее решение. Вы можете скрыть эту строку отgit status / etc, но на самом деле она не игнорируется. Как только вы измените значение, ваш рабочий каталог будет помечен как грязный, даже если изменение может быть не видно.

Если вы действительно хотите, чтобы эта строка вышла из-под контроля версий, единственным практическим средством может быть изменение ее аргумента командной строки или ее размещение в игнорируемом файле include или build.

patricktokeeffe
источник
«Как только вы измените значение, ваш рабочий каталог будет помечен как грязный, даже если изменение может быть не видно»: этого не должно быть, если чистый сценарий все еще восстанавливает файл в его исходном содержании.
VonC
Я так и думал. Я использую GitExtensions, и он покажет файл как измененный, даже если панель различий пуста. Возможно, потому что чистый фильтр не запускается до возврата файла. Возможно, потому что это msysgit, а не git-git. ИДК, YMMV
patricktokeeffe
1

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

Я думаю, что было бы лучше всего иметь файл .userbuildconfig какого-либо типа, который вы просто включаете и устанавливаете значения по умолчанию. Затем вы можете использовать предложение Карлоса, чтобы пометить только этот файл, как предполагаемый без изменений. Таким образом не будут пропущены другие изменения в файле, в котором нужно проверить настройку.

Это может позволить вам настраивать макросы препроцессора локально (или для java что-то вроде этого https://stackoverflow.com/a/1813873/1270965 ).

johnb003
источник
-1

Нет. Вы можете игнорировать только отдельные файлы (и многое другое), поскольку строки в .gitignore соответствуют именам файлов, а не содержимому файла. Вы уже упомянули решение этой проблемы, то есть игнорируйте один файл, содержащий тот контент, который вы хотите игнорировать.

ralphtheninja
источник
Я согласен, в моем случае было достаточно и возможно просто включить этот контент из проигнорированного файла. Так что перемещаем все игнорируемые настройки. там. Кроме того, я создал копию этого проигнорированного файла и добавил ее в индекс в качестве ссылки, поэтому информация о том, что будет в этом файле, не будет потеряна.
Urs