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

Можно ли перезагрузить ветвь Git без изменения моей рабочей копии?

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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. Это, естественно, коснется любых файлов, отличающихся исходными и перегруппированными экспериментальными ветвями, но это определенно правильное поведение. (Конечно, это был не тот пример, который вы дали, но я хочу, чтобы эти инструкции были общими!)

Ответ 2

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

A git репозиторий может поддерживать несколько рабочих деревьев, позволяя вам проверять несколько ветвей за раз.

$ git worktree add ../second-copy experimental
$ cd ../second-copy/
$ git rebase master experimental

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

$ git rebase master experimental

Ответ 3

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

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

Ответ 4

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

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

Вы создаете новый workdir следующим образом:

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

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

Итак, в основном просто:

cd new-workdir
git checkout experimental
git rebase master

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

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


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

# 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, если у вас есть новые файлы, так как иначе они не будут спрятаны. Опять же, не один шаг, но довольно чистый и простой.

Ответ 5

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

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

http://git-scm.com/docs/git-rebase

Ответ 6

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

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

Чтобы проиллюстрировать, предположим, что репозиторий 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.

Прежде чем что-либо делать, вам нужно идентифицировать коммиты в OtherBranch.

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

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

Теперь вы знаете, что базовое коммит - commitC, а последнее commit - 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

Альтернативно (если вы хотите успешно завершить "rebase" перед заменой 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 происходит следующее:

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

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

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

  • Файлы, которые были изменены между базовой ветвью и текущей фиксацией в текущем рабочем дереве.
  • Файлы, которые были изменены коммитами в OtherBranch.

Ответ 7

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

Однако простой способ решить эту проблему - использовать другую рабочую строку для rebase. Просто установите переменную среды 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 ветвь checkout1

git мастер переустановки

git checkout $orgbranch

export GIT_WORK_TREE =

rm -rf/tmp/tmp_wd`