Ответ 1
Как переносить эти изменения в локальную ветвь, которую вы регулярно обновляете/объединяете из своей главной ветки? Таким образом, не было бы никакой опасности совершить их восхождение.
Я использую git, и мне бы хотелось создать коммит, который не синхронизируется с удаленным репозиторием. Такая фиксация должна "плавать" поверх всех других коммитов в локальном репозитории, чтобы избежать влияния на историю. Я мог бы использовать такую фиксацию для хранения локальных изменений (изменения конфигурации, флаги отладки, локальные обходные методы и т.д.).
В настоящее время я вручную переустанавливаю, когда я беру на себя обязательство переупорядочить фиксацию в верхней части, и я нажимаю, используя HEAD^
, чтобы избежать нажатия локальных изменений. Я также рассмотрел возможность внесения изменений в кошелек, но это менее удобно, потому что это препятствует нормальному использованию кошелька. Другой альтернативой является просто оставить все эти локальные изменения неустановленными и использовать git add -p
каждый раз, когда я хочу совершить. Однако при большом количестве тривиальных локальных изменений это становится проблемой.
Вот пример моего текущего рабочего процесса:
Мой репозиторий первоначально выглядит как
A---B---C---F master
где "F" - это моя плавающая фиксация.
Я делаю фиксацию:
A---B---C---F---D master
затем git rebase -i HEAD~2
, чтобы изменить порядок:
A---B---C---D---F master
а затем git push remote HEAD~1...
нажать все, кроме локального F
commit.
Изменение F содержит изменения в существующих версиях файлов и может содержать произвольное количество изменений. (Если я могу сделать более одного "плавающего", это будет еще лучше, поскольку я мог бы затем отделить локальные изменения).
Как переносить эти изменения в локальную ветвь, которую вы регулярно обновляете/объединяете из своей главной ветки? Таким образом, не было бы никакой опасности совершить их восхождение.
Итак, похоже, что вы хотите две вещи:
Некоторые коммиты должны храниться конфиденциально (например, на локальном ветки) и никогда не подталкивать или не сливаться, когда вы тянете; они должны оставаться "после" общих коммитов.
Это должно быть в основном прозрачным для вас; вы хотите работать с мастером, и локальная ветвь поддерживается автоматически. Вы просто решаете, какие коммиты должны быть локальными, а команды, которые взаимодействуют с удаленными репозиториями, будут игнорировать их.
Итак, вы хотите написать script (назовите его git-something
и поместите его на свой путь, чтобы добавить дополнительную команду git) для определения и устранения этих коммитов. Вам понадобится триггер для script, чтобы распознать локальные коммиты. Легкий способ сделать это - поместить волшебное слово в описание фиксации - одно, которое вы никогда не будете использовать в реальном/совместном фиксации - для распознавания script. (Если это слишком громкое звучание для вас, вы также можете использовать специальный файл в дереве фиксации, например .THIS_COMMIT_IS_LOCAL_ONLY
; я не делал этого в примерах, потому что это немного сложнее.)
Вам понадобится команда, чтобы сделать локальную фиксацию из текущего индекса /workdir; это просто, он просто вызывает git commit [email protected] -m "__LOCAL_COMMIT_ONLY__"
(это пример: точка в том, что он делает что-то, чтобы пометить, что коммит создается как локальный, а затем отменил git commit). Вам также понадобится команда временно вывести все локальные коммиты, выполнить еще одну команду git (pull, push, fetch, merge, whatever), а затем повторно применить локальные коммиты. Вы также будете использовать эту команду для создания локальных коммитов, которые вы собираетесь использовать, чтобы они всегда отображались "под" локально только в вашей истории.
Здесь один пример script, который дает вам оба в одном:
#!/bin/sh
if [[ $1 eq 'new' ]]; then
shift
exec git commit [email protected] -m "__LOCAL_COMMIT_ONLY__"
elif [[ $1 eq
OLD_HEAD=$(git rev-parse HEAD)
OLD_REAL_HEAD="$(git rev-list --grep=__LOCAL_COMMIT_ONLY__ | tail -n1)^"
git reset --soft $OLD_REAL_HEAD
git [email protected]
git rebase --onto HEAD $OLD_REAL_HEAD $OLD_HEAD
Теперь, предположив, что вы вызываете script git-local
, используйте git local new
для создания нового локального коммита из индекса (или git local new -a
для создания из измененных файлов в workdir), git local commit
(имя несовершенно, грустно), чтобы создать новую "реальную" фиксацию, git local push
нажать, git local pull
, чтобы вытащить и т.д.
Основной недостаток этого заключается в том, что он требует, чтобы вы помнили, что большинство команд теперь имеют префикс с local
. Если вы забудете сделать это один раз, вы будете немного взволнованы, но не так уж плохо - быстрый git rebase -i
позволит вам легко переместить свои локальные коммиты обратно в начало, а вы снова запустите и запустите. Самый большой риск состоит в том, что вы случайно используете git push
вместо git local push
и отправляете все свои личные изменения вверх по течению, что будет раздражать всех. Для этого вам может понадобиться написать небольшую оболочку script для вызова вместо самой git (назовите ее ~/bin/git
и убедитесь, что ~/bin
находится на вашем пути):
#!/bin/bash
if [[ $1 eq 'push' ]]; then
if git rev-list --grep=__LOCAL_COMMIT_ONLY__ | grep -q .; then
echo "Can't push with local changes still active!"
echo "Try using `git local push' instead."
exit 1
fi
fi
exec /usr/bin/git "[email protected]"
Вы также можете сделать крючок pre-receive
на сервере, который автоматически отклоняет любое сообщение, содержащее __LOCAL_COMMIT_ONLY__
в своем сообщении.
На предыдущей работе у каждого была своя локальная ветвь settings
, на которой мы выполнили наши индивидуальные настройки. Эта ветвь была основана на master
; любая ветвь темы была разветвлена settings
. Когда темы были готовы к интеграции, мы переустановили их на master
. Это похоже на то, что предлагает @koljaTM.
A---B---C master
\
F settings
\
D topic
rebase --onto master settings topic
A---B---C master
|\
| F settings
\
D topic
Когда новые изменения попали на master
, мы бы rebase settings
из master
, а затем пересобирали любые темы, над которыми мы работали, с settings
.
По общему признанию, это не одношаговое решение "оставить это плавающее навсегда", но оно достаточно чистое и простое.
Вы можете использовать файл исправления, который может быть сгенерирован из Git.
# git diff > local.patch
Если вы хотите зафиксировать, обратный патч:
# git apply -R local.patch
После фиксации восстановить патч:
# git apply local.patch
Вам нужно только убедиться, что только локальные изменения находятся на рабочем дереве перед созданием файла исправления. После создания файла патча вы можете добавить его в .gitignore
, чтобы вы не зафиксировали его.
Предполагая, что у вас есть локальные изменения в файле, который не содержит других изменений, которые необходимо выполнить, вы можете просто попросить git игнорировать изменения в этом файле с помощью git update-index. В частности, вам нужно либо - нецелевое, либо -skip-worktree; в то время как они в основном делают то же самое (отметьте файл неизменным в git index), у них есть некоторые очень тонкие особенности, которые хорошо описаны здесь.
Если вы действительно пытаетесь предотвратить движение вперед, вы находитесь на правильном пути - используйте
git rebase -i
и переупорядочить все ваши "личные коммиты" как последние коммиты, затем
git push <remotename> <latest commit SHA you want to push>:<remotebranchname>
Это подтолкнет все коммиты до и включая предоставленную вами SHA, но не "личные коммиты", которые появляются после нее.
Вы можете игнорировать определенные файлы в git, чтобы они не попадали в удаленный репозиторий. То, как я делаю такие вещи, - это сохранить эти файлы локально и не вставлять их в репозиторий, игнорируя их.
Вот некоторое объяснение гитиор.
Я постоянно сталкивался с этой проблемой, как правило, с разделяющими настройками IDE и т.п. с общей кодовой базы. Это достаточно распространенный случай использования, который вы могли бы подумать, что он был бы разрешен к настоящему времени, но еще нет. Я не считаю, что git
может обрабатывать это, как вы хотите.
Проблема, сводящаяся к сути, состоит в том, что вы хотите, чтобы две проверки делились одним каталогом. Для того, чтобы это сработало, должна быть какая-то структура данных, которую можно манипулировать пользователем, помечая, какие файлы принадлежат к какому-либо из проверок. И это не соотношение 1-1; вполне разумно иметь некоторую часть файла более чем одной проверки. Во всех случаях вам нужно каким-то образом называть различные проверки, чтобы поддержать неоднозначность. Для элементарного здравомыслия вам понадобится каталог lister, который аннотирует файлы со своими ассоциациями проверки.
В качестве конкретной иллюстрации рассмотрим .gitignore
. Это один файл, который применяется ко всем "проверкам", что подразумевает неявно "уникальную проверку". Для того, чтобы .gitignore
работал хорошо, вам понадобится один для каждой проверки, чтобы различать разные файлы в каждой проверке. Тем не менее, нет намека на такую инфраструктуру в git
.
Мой "стандартный" способ решения таких проблем состоит в том, чтобы упорядочить вещи так, чтобы одна проверка в каталоге. Я создаю каталог под названием "custom" (или тому подобное). Этот каталог переходит в .gitignore
в родительском. В пользовательском каталоге я отправляю чек из другого репозитория и/или другого места. Это означает, что два фиксируют все изменения; что редко является проблемой на практике.
Если это заставляет кого-либо взломать git
, добавьте параметр, чтобы сделать "именованный контроль" и оставить существующее поведение анонимным. У вас могут быть анонимные выписки [0..1] и [0.. *). Исходя из этого, большинство других вещей, которые вы, возможно, захотите, следует довольно естественно.