Могу ли я перебазировать ветку Git без изменения моей рабочей копии? - PullRequest
53 голосов
/ 06 февраля 2011

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

Если бы не требование «не изменять мою рабочую копию», это было бы просто делом:

# current branch is master
git checkout experimental
git rebase master
git checkout master

Моя настоящая проблема заключается в том, что это изменяет временные метки в моей рабочей копии, хотя я заканчиваю проверкой того же контента, с которого начал.Как только я запустил «git checkout экспериментальный», для всех файлов, которые содержат изменения в экспериментальной ветке, будет установлено текущее время mtime, равно как и для всех файлов, которые были изменены в master с тех пор, как я в последний раз перебирал экспериментальный.Поскольку mtimes изменились, такие вещи, как инструменты сборки, понимают, что есть работа, которую им нужно сделать снова, хотя к тому времени, когда я закончу, содержимое файлов фактически не изменилось.(В моем случае это то, что если метка времени файла проекта изменяется, Visual Studio считает, что необходимо потратить много времени на выгрузку и перезагрузку проекта.) Я хочу этого избежать.

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

(Если есть конфликты, я бы предпочел показать ошибку, а затем прервать всю операцию, не изменяя временные метки. Но это только мое предпочтение, а не жесткое требование - я незнать, что все возможно.)

Конечно, я могу написать скрипт для захвата mtimes, запустить git и затем сбросить mtimes;но похоже, что в Git уже есть способ сделать что-то вроде rebase, не беспокоясь о рабочей копии, так как rebase на самом деле касается дельт, а не фактического содержимого файлов.

Ответы [ 7 ]

32 голосов
/ 06 февраля 2011

Есть ли способ сделать все вышеперечисленное за один шаг, ничего не изменяя в рабочей копии?

Это, к сожалению, невозможно (без создания изменяемой копии рабочей копии - см. Также ответ Петра ), потому что git выполняет все операции слияния-y (реальные слияния, выборки вишни, ребазировки, исправление приложения ) на рабочем дереве. Это уже упоминалось несколько раз, например, в одном из ответов знающего Якуба Наренбского :

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

Да, это дизайнерское решение, но оно довольно понятное - было бы непросто создать всю структуру, необходимую для попытки слияния в памяти, а затем, как только возникнет конфликт, сбросить все в рабочем дереве, когда вместо этого вы могли бы просто сделать это в рабочем дереве в первую очередь. (Я не git-разработчик; не воспринимайте это как абсолютную полную правду. Могут быть и другие причины.)

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

git clone project project-for-rebase
cd project-for-rebase
git branch experimental origin/experimental
git rebase master experimental
git push origin experimental

Это, конечно, предполагает, что эксперимент не проверен в вашем первоначальном репо. Если это так, вместо нажатия вы бы сделали что-то вроде git fetch ../project-for-rebase experimental; git reset --hard FETCH_HEAD или более читабельное, git remote add for-rebase ../project-for-rebase; git fetch for-rebase; git reset --hard for-rebase/experimental. Это будет естественно затрагивать любые файлы, отличающиеся между исходной и перебазированной экспериментальными ветками, но это определенно правильное поведение. (Конечно, это был не тот пример, который вы привели, но я хочу, чтобы эти инструкции были общими!)

29 голосов
/ 30 января 2016

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

Репозиторий git может поддерживать несколько рабочих деревьев, что позволяет вам проверять более одной ветви за раз.После этого вы можете rm -rf second-copy, если хотите, или оставить его для дальнейших перебазировок в будущем.

$ git rebase master experimental
11 голосов
/ 18 сентября 2012

Я создал небольшой скрипт для Linux. Он основан на ответе Джефроми и нескольких дополнениях (главным образом, установка альтернатив, чтобы база данных объектов не копировалась, а только извлекала нужные ветви) Некоторые из вас могут найти это полезным: https://github.com/encukou/bin/blob/master/oot-rebase

Если это не совсем то, что вам нравится, запросы на получение приветствуются:)

5 голосов
/ 13 октября 2015

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

https://github.com/git/git/blob/master/contrib/workdir/git-new-workdir

Вы создаете новый рабочий каталог, например:

git-new-workdir project-dir new-workdir branch

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

Так что в основном просто:

cd new-workdir
git checkout experimental
git rebase master

Не совсем одна команда, но довольно простая.

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


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

# current branch is master (with changes to working state)
git stash -u
git checkout experimental
git rebase master
git checkout master
git stash pop

Обязательно используйте stash -u, если у вас есть какие-либо новые файлы, иначе они не будут сохранены. Опять же, не один шаг, но довольно чисто и просто.

1 голос
/ 23 августа 2017

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

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

Для иллюстрации предположим, что Git-репозиторий имеет следующую структуру:

repo
- commitA
- commitB
- commitC <-- master <-- OtherBranch based on master
  - commitD <-- First commit in otherBranch
  - commitE <-- Second commit in OtherBranch
- commitD <-- Unrelated commit in current working tree

. Для примера предположим, что «OtherBranch» разветвляется от «master»,и что ваше текущее рабочее дерево также основано на "мачтеэр».Ваш рабочий процесс обычно начинается с обновления локальной ветки master с удаленной версией ...

# Fetch commits from remote "origin" and update the master branch:

# If your current branch is identical to master
git pull origin master

# If your current branch has extra commits on top of master
git pull --rebase origin master

# If you don't want to touch your current branch
git fetch origin master:master

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

Перебазирование другой ветви (справочный пример - НЕ делайте этого)

Следующее решение - это git способ сделать это:

git checkout OtherBranch
git rebase master    # or git rebase origin/master

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

Перебазирование другой ветви с минимальными изменениями

Чтобы свести к минимуму количество файлов, к которым вы обращались, необходимо перейти к новой базовой ветви, а затем применить все дополнительные коммиты в OtherBranch поверх базовой ветви, используя git cherry-pick.

* 1031.* Прежде чем что-либо делать, вам нужно определить коммиты в OtherBranch.
  • git log OtherBranch показывает коммиты на OtherBranch (в основном полезно, если вы еще не изменили OtherBranch)
  • git reflog показывает изменения веток в вашем локальном репозитории (полезно, если вы уже обновили ветки и допустили ошибку).

В текущем примере вы обнаружите, что lasOtherBranch - это commitE.Вы можете увидеть список коммитов до этого с помощью git log commitE (или, если вы хотите более короткий список, git log --oneline commitE).Если вы посмотрите список, вы увидите, что базовый коммит равен commitC.

Теперь вы знаете, что базовый коммит - commitC, а последний коммит - commitE, вы можете перебазировать OtherBranch (от предыдущего «мастера» к новому «мастеру») следующим образом:

# Replace the old OtherBranch with "master" and switch to it.
git checkout -B OtherBranch master

# Cherry-pick commits starting from commitC and ending at commitE.
cherry-pick commitC^..commitE

В качестве альтернативы (если вы хотите успешно завершить «ребазирование» перед заменой OtherBranch):

# Create new branch NewOtherBranch based off "master" and switch to it.
git checkout -b NewOtherBranch master

# Cherry-pick commits starting from commitC and ending at commitE.
cherry-pick commitC^..commitE

# Replace the old branch with the current branch (-M = --move --force)
git branch -M OtherBranch

Почему это работает?

Перебазирование веток в git требует переключения текущей ветки на ветку, которую вы хотите обновить (OtherBranch).

С git rebaseВ рабочем процессе происходит следующее:

  1. Переключение на OtherBranch (возможно, разветвленная очень старая базовая ветвь).
  2. Перебазировка (внутренний шаг 1): сохранение коммитов, которые не находятся ввосходящая ветвь.
  3. Rebase (внутренний шаг 2): сброс текущей ветви к (новой) базовой ветке.
  4. Rebase (внутренний шаг 3): восстановление фиксаций с шага 2.

Шаг 1 и шаг 3 касаются многих файлов, но в конечном итоге многие изфайлы hed фактически не изменились.

Мой метод объединяет шаги 1 и 3 в шаг 3, и в результате количество файлов, к которым обращаются, минимально.К этим файлам обращаются только следующие файлы:

  • Файлы, которые были изменены между базовой веткой и текущим коммитом в текущем рабочем дереве.
  • Файлы, которые были изменены коммитами вOtherBranch.
1 голос
/ 06 февраля 2011

Мне бы тоже понравилось, но это не оставляет для меня надежды:

Если указано <branch>, git rebase выполнит автоматическую проверку git прежде чем делать что-либо еще. В противном случае он остается на текущем филиал.

http://git -scm.com / документы / ГИТ-перебазироваться

0 голосов
/ 08 августа 2013

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

Однако, простой способ решить эту проблему - использовать другое рабочее дерево для перебазирования.Просто установите переменную окружения GIT_WORK_TREE для другого рабочего дерева.Только не забывайте, чтобы ваш HEAD соответствовал вашему рабочему дереву.

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

`orgbranch = $ (git rev-parse HEAD)

mkdir / tmp / tmp_wd

cp -r! (. Git) / tmp / tmp_wd

export GIT_WORK_TREE = / tmp / tmp_wd

git checkout branch1

мастер git rebase

git checkout $ orgbranch

export GIT_WORK_TREE =

rm -rf / tmp / tmp_wd`

...