Игнорировать определенные изменения в файле в git, но не весь файл

У меня есть файл в репозитории git, в котором есть локальные изменения. Я хочу, чтобы git игнорировал локальные изменения навсегда, но не файл. Особенно,

  • Если файл не затрагивается, кроме этого изменения, git add . никогда не должен ставить это.
  • Точно так же, git commit -a не должен совершать это.
  • Если я когда-нибудь внесу дополнительное изменение в файл, я смогу подготовить и зафиксировать это изменение, но игнорируемое мной изменение не следует ставить и фиксировать.

Есть ли способ сделать это? Проводя некоторые исследования, я читал о "размазывании / чистых циклах", где, если я правильно понял,

  1. файл будет помечен как неизмененный,
  2. внесенные мной изменения будут перезаписаны при оформлении заказа,
  3. и затем скрипт автоматически повторно применяет изменения, а затем снова помечает файл как неизмененный.

Хотя я очень новичок в git и сценариях (я стажер с C# и опытом Java), так что если это то, что мне нужно сделать, вы можете опубликовать подробные инструкции или ссылку на учебник о том, как установить пятно / очистить цикл?

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

10 ответов

Решение

На мой взгляд, описываемая вами ситуация такова: вы хотите поддерживать рабочий каталог, отличный от локального хранилища.

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

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

git checkout -b new-branch
# now you can change the file without affecting master
echo bar >> foo
git add
git commit
# and back to master
git checkout master

Вы можете использовать skip-worktree немного. Включите его с помощью:

git update-index --skip-worktree <file>

После этого git никогда не будет вносить локальные изменения для <file> и потерпит неудачу (громко), если Git сам должен написать <file> (скажем, в слиянии или проверке).

Если вы когда-нибудь захотите подготовить изменение в будущем, вы можете отключить его, включить новое изменение, а затем снова включить:

git update-index --no-skip-worktree <file>
git add -p <file>
git update-index --skip-worktree <file>

Хотя это и не идеально, это может быть достаточно хорошо. Это будет зависеть от вас, чтобы заметить, что <file> не внесенные изменения, так как git больше не скажет вам, что

Примечание: мое первоначальное предложение заключалось в использовании assume-unchanged, Как объясняется в Git - Разница между "предположим, без изменений" и "пропустить рабочее дерево", это действительно skip-worktree что ты хочешь. Особенно, assume-unchangedэто обещание Git, что вы не будете изменять файл, и если вы нарушите это обещание, Git может стереть ваши изменения или зафиксировать их! В отличие от Git не будет стирать или совершать ваши skip-worktree изменения.

Эти ответы хороши, но не могут решить проблему Кевина. У меня была похожая проблема, часто редактируя файл конфигурации, чтобы приложение, над которым я работал, получало доступ к моей собственной частной базе данных разработки вместо рабочей. Это всего лишь вопрос времени, когда я случайно зарегистрируюсь и внесу изменения в конфигурацию! Мне просто нужен легкий способ игнорировать файл. Вот что я узнал:

  • Сначала внесите необходимые изменения в свой файл. Я позвоню my_config,

  • Сделайте файл патча этого изменения с git diff >../somewhere-else/my_config.patch

  • Теперь скажите git, чтобы он игнорировал этот файл (без необходимости изменения зарегистрированного.gitignore): git update-index --assume-unchanged my_config

Теперь, если вы не вносите изменения в my_config что вы хотите зарегистрироваться, вы можете работать свободно. Перестать игнорировать my_config, делать git update-index --no-assume-unchanged my_config, После внесения чужих изменений в my_config, вы можете легко восстановить ваши личные изменения с git apply ../somewhere-else/my_config.patch, затем... примите-снова без изменений, как указано выше, и вернитесь к работе!

Вот несколько полезных псевдонимов, которые вы можете добавить в ~/.gitconfig:

[alias]
    unchanged = update-index --assume-unchanged
    changed = update-index --no-assume-unchanged
    show-unchanged = !"git ls-files -v | sed -e 's/^[a-z] //p; d'"

"Режим патча" в Git идеально подходит для добавления только определенных изменений из файла в ваш коммит.

Чтобы начать, наберите git add -p, git commit -p (прямо, чтобы сделать сообщение, когда сделано), или git add --interactive (больше подсказок).

По сути, он проведет вас через каждый раздел кода, показанный в git diff и спрашивает вас, хотите ли вы поставить его или нет.

Когда вы достигнете изменения, либо ответьте n o, либо e, чтобы открыть патч в вашем $EDITOR.

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

Вот метод, который мне нравится:

  1. Сделайте некоторые изменения, которые вы хотите сохранить на локальном компьютере.
  2. использование git branch local а также git checkout local сделать новую ветку для ваших локальных изменений. (Если я не ошибаюсь, эти команды не будут устанавливать удаленную ветвь автоматически, поэтому новая ветвь не будет выдвинута.)
  3. использование git commit совершить ваши изменения.
  4. Внесите изменения, которые вы хотите подтолкнуть вверх по течению.
  5. использование git checkout parent чтобы вернуться в ветку, которую вы хотите подтолкнуть.
  6. использование git commit совершить эту ветку.
  7. использование git push подтолкнуть ваши изменения.
  8. использование git checkout local чтобы вернуться в местное отделение.
  9. использование git rebase parent внести изменения из parent в к local, С помощью rebase сделает так, чтобы ваши локальные изменения оставались поверх изменений в parent,
  10. Вернитесь к шагу 4 и повторите до тошноты.

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

Да, это может быть сделано с помощью грязных / чистых фильтров. Однако я настоятельно рекомендую не делать этого, потому что это будет довольно сложно и подвержено ошибкам (например, это может привести к путанице во многих инструментах, основанных на git, затруднить отладку и т. Д.).

Кроме того, обычно не стоит иметь постоянные локальные изменения. Одним из важных моментов использования SCM является то, что вы можете проверить версию и сразу же начать работать. Это означает, что все, что вам нужно, должно быть проверено.

Я не думаю, что есть "хороший" способ сделать это, ни с помощью git, ни, вероятно, с большинством других SCM. Я бы порекомендовал вам пересмотреть ваши требования.

Похоже, проблема в том, что у вас есть файл со "смешанным содержимым": часть содержимого всегда одинакова, а часть необходимо изменить локально (параметры, специфичные для машины?). Рекомендуемый способ справиться с этим - не проверять проблемный файл, а вместо этого проверять файл "шаблона", а затем генерировать реальный файл во время сборки. Попробуйте расследовать это. Сейчас будет больше работы (возможно), но это облегчит задачу, особенно если вам нужно поддерживать больше вариантов или если другие люди хотят работать над тем же проектом.

Изменить (на основе информации в комментарии)

Вы пишете, что хотите игнорировать локальное изменение кода, которое необходимо только для машин разработки, но не в производстве.

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

Еще лучше, просто исправьте ошибку. Если ошибка усложняет разработку, то ИМХО она является приоритетной.

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

(1) В качестве примера: в нашей компании у нас есть переменная окружения специально для подобных случаев. Он указывает, выполняется ли код в разработке, в бета-тестировании или в производстве, и задается нашими сценариями сборки и развертывания. Тем не менее, он обычно используется только для простых вещей, таких как выбор разных путей к файлам. Изменение логики программы в зависимости от среды не рекомендуется, как объяснено выше.

Поскольку ни один из ответов меня полностью не удовлетворил, и ИМХО не совсем соответствовал всем требованиям ОП (которые аналогичны моим), я хотел бы поделиться своим решением.

Предварительный

Для меня ежедневно вносить некоторые изменения в мой локальный репозиторий, которые не должны передаваться на удаленный сервер (соединения с базой данных, адаптации для моей локальной тестовой установки и т. д.). Эти изменения всегда появлялись в сообщении о состоянии как измененные, и раньше мне приходилось быть осторожным с тем, что регистрировать (поскольку я привык использовать git add --all), чтобы эти изменения не были отправлены на удаленный.

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

Решение

Я создал файл исправления с изменениями, отменил их перед фиксацией, а затем повторно применил.

Вот мое пошаговое руководство для этого

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

    git diff fileA FileB FileC > ../somePathOutsideYourGit/001-debugSetupChanges.patch

  2. Создайте псевдоним git для добавления всех файлов в промежуточную область. Добавьте следующую строку в файл конфигурации git в разделе [alias].

    aa = "!git apply -R /home/user/somePathOutsideYourGit/001-debugSetupChanges.patch && git add --all && git apply /home/user/somePathOutsideYourGit/001-debugSetupChanges.patch"

Вы можете получить доступ к конфигурации с помощьюgit config --global --edit

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

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

      `st = "!git apply -R /home/user/somePathOutsideYourGit/001-debugSetupChanges.patch && git status && git apply /home/user/somePathOutsideYourGit/001-debugSetupChanges.patch"`

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

Начиная с Git 2.5 (июль 2015 года), вы можете использовать git worktree:

git worktree add -b patch-1 ../patch-1

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

Если вы используете intellij в качестве IDE, то у него есть удобная функция (списки изменений), которая, как я считаю, делает именно то, что запрашивает OP.

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

Если вы не можете использовать.gitignore, так как вам нужно вносить параллельные изменения в один и тот же файл (как указано в ваших комментариях), тогда один из вариантов - git add -p. С его помощью вы можете добавить или пропустить соответственно.

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

Другие вопросы по тегам