Есть ли способ `git stash` для каждого файла в проекте (не только для изменений)? - PullRequest
2 голосов
/ 08 июня 2019

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

Вот краткий обзор моего рабочего процесса Git:

  • Я храню локальную копию нашей ветки origin / master на моей машине, которую я буду называть local / master.
  • Я использую local / master для сравнения при отладке изменений в моих собственных локальных / функциональных ветках. Например, если я заметил ошибку в моей локальной / функциональной ветке, я мог бы переключиться обратно на локальную / основную ветку, чтобы посмотреть, воспроизводится ли она там. Это позволяет мне узнать, существовала ли ранее ошибка или была вызвана моими собственными изменениями.
  • Я также использую local / master в качестве промежуточной промежуточной ветви при выполнении слияний между origin / master и моей локальной / feature ветками. Это препятствует тому, чтобы исходная ветвь (master) была движущейся целью в случае, если мне нужно отказаться от слияния и начать заново.
  • Я обновляю только local / master, вытягивая из origin / master. Это делается всякий раз, когда другие разработчики объединяют свои собственные изменения в origin / master.
  • Я отправляю свой код, нажимая от локального / объекта к источнику / объекту и отправляя запрос на объединение из источника / объекта в источник / мастер.

Вот хронологические шаги, которые привели к этой ситуации:

  1. Я разветвлял ветку от local / master как local / feature1.
  2. Я сделал несколько коммитов на local / feature1.
  3. Функция была объединена в origin / master, которая перемещала некоторые файлы .pem (открытый / закрытый ключи) из одного каталога в другой.
  4. Добавлен хук к origin (частный экземпляр Gitlab), который препятствовал регистрации любых последующих файлов .pem.
  5. Я вытащил из источника / мастера в локальный / мастер.
  6. Я слился с локальным / master в local / feature1.
  7. Я сделал еще несколько коммитов для local / feature1.
  8. Я попытался выдвинуть свой код из local / feature1 в источник как origin / feature1.
  9. origin жаловался, что мне не разрешили регистрировать файлы .pem.

Я полагаю, что это происходит, потому что история коммитов в local / feature1 теперь содержит коммит слияния, который включал изменения файла .pem. Этот файл был добавлен в origin / master до реализации ловушки, поэтому на него не распространялись ограничения ловушки. Тем не менее, коммит слияния в мою ветку отправляется позже вместе с моими изменениями кода, поэтому он помечается крючком.

На данный момент я хотел бы удалить файлы .pem из истории коммитов local / feature1 без фактического удаления файлов. Перебазирование и сжатие коммитов в этот момент не решит проблему, так как файлы .pem уже есть. Удаление файлов .pem не поможет, поскольку они действительно должны быть в проекте (несвязанная история). Возврат моей ветки в нераспределенное состояние также не вариант, так как загруженный источник / функция в таком случае будет слишком устаревшим с источником / мастером для обзора, чтобы иметь смысл.

Очевидное решение выглядит следующим образом:

# Copy current state of the project to a backup directory
cp -r . ../backup
rm -rf ../backup/.git

# Reset project to latest official state
git checkout local/master
git pull

# Regenerate local branch so the merged changes aren't included as local commits
git branch -D local/feature1
git checkout -b local/feature2

# Copy the desired project state back in and commit it
cp -rf ../backup/. .
git add --all
git commit -m "Regenerating feature branch"
rm -rf ../backup

# Upload without being restricted by hook
git push --set-upstream origin feature2

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

Ответы [ 2 ]

3 голосов
/ 08 июня 2019

Здесь нет необходимости git stash. git stash уже сохраняет полные снимки, но также и все коммиты. Без коммитов это diff. Git превращает коммит в diff, сравнивая его с другим полным снимком: что бы ни отличалось в двух снимках, это diff. Многие команды, включая git stash, делают это много, сравнивая коммит с его родителем (-ями). Например, git cherry-pick сравнивает выбранный коммит с его родителем, чтобы увидеть, что изменилось, и затем применяет эти изменения, где бы вы ни находились (также выполняя второй diff и используя механизм слияния).

A git commit делает свой снимок из содержимого index , а не из того, что находится в рабочем дереве. Итак, если вы хотите новый коммит в какой-либо существующей ветви, который точно соответствует любому существующему коммиту, все, что вам нужно сделать, это удалить все в индексе и заменить все на все из какого-либо другого коммита. (Рабочее дерево подходит для поездки, чтобы вы могли видеть, что происходит.) Существует очевидный простой способ сделать это:

$ git checkout br1
$ git rm -r .             # from the top level: remove everything
$ git checkout br2 -- .   # extract everything from commit at tip of br2
$ git commit

Это делает новый коммит, который добавляет к кончику br1, содержимое которого от br2. (Необнаруженные неотслеживаемые файлы не появятся, поскольку неотслеживаемые файлы по определению отсутствуют в индексе, но следует соблюдать осторожность в отношении файлов, отслеживаемых в извлеченном коммите, которые не были отслежены в старой подсказке br1, и наоборот.)

Существует более короткая версия того же:

$ git read-tree -m -u br2
$ git commit

, что вызывает меньший отток в рабочем дереве (иногда это важно, например, для make): read-tree считывает фиксацию в индексе, а -u заставляет Git обновлять работу Дерево, чтобы соответствовать. Поскольку здесь есть только один аргумент для git read-tree, он выбрасывает текущее содержимое индекса, удаляя любой файл, который находится в нем (и в рабочем дереве), который не находится в коммите br2. Как и в случае с более длинным вариантом, это не влияет на неотслеживаемые файлы.

2 голосов
/ 08 июня 2019

Я не понимаю, почему перебазирование local / feature1 поверх local / master (на шаге 6) недостаточно. Он внесет изменения pem из апстрима, воспроизведет ваши коммиты поверх него, поэтому при нажатии кнопки pem не будут затронуты.

Вам придется нажать --force, если вы ранее нажали на origin / feature1, но функция принудительного нажатия должна быть в порядке.

...