Подтвердить что ты не робот

Как выполнить "локальное коммит" в git?

Я использую 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 содержит изменения в существующих версиях файлов и может содержать произвольное количество изменений. (Если я могу сделать более одного "плавающего", это будет еще лучше, поскольку я мог бы затем отделить локальные изменения).

4b9b3361

Ответ 1

Как переносить эти изменения в локальную ветвь, которую вы регулярно обновляете/объединяете из своей главной ветки? Таким образом, не было бы никакой опасности совершить их восхождение.

Ответ 2

Итак, похоже, что вы хотите две вещи:

  • Некоторые коммиты должны храниться конфиденциально (например, на локальном ветки) и никогда не подталкивать или не сливаться, когда вы тянете; они должны оставаться "после" общих коммитов.

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

Итак, вы хотите написать 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__ в своем сообщении.

Ответ 3

На предыдущей работе у каждого была своя локальная ветвь 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.

По общему признанию, это не одношаговое решение "оставить это плавающее навсегда", но оно достаточно чистое и простое.

Ответ 4

Вы можете использовать файл исправления, который может быть сгенерирован из Git.

# git diff > local.patch

Если вы хотите зафиксировать, обратный патч:

# git apply -R local.patch

После фиксации восстановить патч:

# git apply local.patch

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

Ответ 5

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

Ответ 6

Если вы действительно пытаетесь предотвратить движение вперед, вы находитесь на правильном пути - используйте

git rebase -i

и переупорядочить все ваши "личные коммиты" как последние коммиты, затем

git push <remotename> <latest commit SHA you want to push>:<remotebranchname>

Это подтолкнет все коммиты до и включая предоставленную вами SHA, но не "личные коммиты", которые появляются после нее.

Ответ 7

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

Вот некоторое объяснение гитиор.

https://help.github.com/articles/ignoring-files

Ответ 8

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

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

В качестве конкретной иллюстрации рассмотрим .gitignore. Это один файл, который применяется ко всем "проверкам", что подразумевает неявно "уникальную проверку". Для того, чтобы .gitignore работал хорошо, вам понадобится один для каждой проверки, чтобы различать разные файлы в каждой проверке. Тем не менее, нет намека на такую ​​инфраструктуру в git.

Мой "стандартный" способ решения таких проблем состоит в том, чтобы упорядочить вещи так, чтобы одна проверка в каталоге. Я создаю каталог под названием "custom" (или тому подобное). Этот каталог переходит в .gitignore в родительском. В пользовательском каталоге я отправляю чек из другого репозитория и/или другого места. Это означает, что два фиксируют все изменения; что редко является проблемой на практике.

Если это заставляет кого-либо взломать git, добавьте параметр, чтобы сделать "именованный контроль" и оставить существующее поведение анонимным. У вас могут быть анонимные выписки [0..1] и [0.. *). Исходя из этого, большинство других вещей, которые вы, возможно, захотите, следует довольно естественно.