фиксация в ветке, которая не проверена - PullRequest
4 голосов
/ 20 марта 2010

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

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

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

Ответы [ 3 ]

12 голосов
/ 20 марта 2010

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

# make a new branch starting from branch machine_1
git checkout -b move_to_master      
# make whatever commits you need to
git rebase --onto master machine_1 move_to_master
git checkout master
git merge move_to_master  # this is a fast-forward
git checkout machine_1
git merge master

Если вы случайно зафиксировали machine_1 перед созданием move_to_master, просто создайте move_to_master, затем верните machine_1 туда, где он принадлежит, и выполните остальные шаги.

Однако на ваш вопрос стоит ответить, и я предоставил еще пару вариантов внизу.

Создание коммитов вне текущей ветки

Меры предосторожности: будь очень, очень осторожен! Это страшная штука!

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

git commit-tree -p $PARENT_COMMIT < $COMMIT_MESSAGE_FILE

Это напечатает на стандартный вывод SHA1 вновь созданного объекта коммита; предполагая, что PARENT_COMMIT был подсказкой ветки, вы должны затем использовать git update-ref , чтобы обновить ветку к ней:

git update-ref -m "commit: [commit subject]" $BRANCH $NEW_SHA1

Если вы пишете этот сценарий, вы можете достичь этого в одну строку как git update-ref -m ... $(git commit tree ...). Это самый страшный шаг. Если вы обновите вашу другую ветку в неправильном месте, это будет отстой. Вы все еще можете выяснить, куда его вернуть, используя git reflog show $BRANCH.

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

  • git read-tree - считывает информацию о дереве в индекс, но совсем не обновляет рабочее дерево (git checkout примерно эквивалентен git read-tree, git checkout-index и git update-ref HEAD). Вы можете использовать это, чтобы индекс содержал содержимое этой не выписанной ветви вместо HEAD.
  • git update-index - изменяет индекс. Вы можете использовать это для добавления, удаления или обновления файлов в индексе из рабочего дерева.
  • git checkout-index - скопировать заданные пути из индекса в рабочее дерево. После использования дерева чтения вы можете использовать его, чтобы получить отдельный файл, который вы хотите изменить, изменить его, а затем вернуть обратно с помощью update-index. Шаг «изменить его» может быть сложным - например, перед тем, как сделать все это, вы можете создать патч с помощью git diff, а затем применить его с помощью git apply.
  • git apply --cached При использовании опции --cached патч применяется непосредственно к версии в индексе, не касаясь рабочего дерева. Поэтому вы можете создать diff, прочитать дерево другой ветви, применить его к индексу, commit-tree, и все готово. Это, наверное, самый удивительный способ сделать это.

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

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

Альтернативные подходы

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

  • клонировать ваш репозиторий. Если вы отслеживаете только файлы конфигурации, это не займет много дополнительного пространства, и все будет намного проще. Если вы действительно одержимы, вы можете даже использовать сценарий git new-workdir (ссылка на версию в HEAD git.git), чтобы сделать только рабочий каталог, не дублируя остальную часть репозитория ( он использует символические ссылки в каталоге .git). Просто помните, что нужно соблюдать осторожность при фиксации в одном рабочем каталоге ветки, которая была извлечена в другом - у другого в конечном итоге будет работать дерево синхронизации не синхронизировано.

  • написать скрипт-обертку с одним коммитом - это самое близкое к созданию одного коммита в другой ветви из всех этих опций:

    git commit
    orig_branch=$(git symbolic-ref HEAD)
    orig_branch=${orig_branch#refs/heads/}
    git checkout master
    git cherry-pick $orig_branch
    git checkout $orig_branch
    git reset --hard HEAD^
    
1 голос
/ 28 марта 2010

Я делаю это для моего проекта Visual Git Reference. Я запускаю make gh-pages, который создает сайт и фиксирует его в ветке gh-pages, без необходимости переключать ветки. Смотрите мои репозиторий GitHub , в частности файлы Makefile и publish . Я, вероятно, должен использовать $GIT_INDEX_FILE, как упоминал Крис Джонсен, но это, кажется, работает нормально.

0 голосов
/ 20 марта 2010

Возможно, я неверно истолковал ваш запрос, но дайте мне знать, если это то, что вы ищете.

Вы начинаете с машинно-ориентированной ветки.Вы вносите изменения, но вместо фиксации в машинно-зависимой ветке вы хотите зафиксировать изменение в общей ветке.Что вы должны сделать, это

git stash              # save your changes for later
git checkout common    # switch to the common branch
git stash apply        # recall the stashed changes
git commit             # commit as appropriate (on the common branch)
git stash drop         # kill off the last stashed changes (this is optional)
git checkout machine1  # go to your machine-specific branch
git rebase common      # put your machine-specific changes after the common ones
git checkout machine2  # [repeat]
git rebase common

В качестве альтернативы, вы можете использовать git merge вместо git rebase, если ваши изменения неприменимы.

Применимо ли это к вашей ситуации?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...