Команда git для создания одной ветви как другой
Я пытаюсь взять ветку с изменениями и вернуть ее так, чтобы она была идентична той ветке, от которой она отклонилась. Изменения являются как локальными, так и перенесены в github, поэтому ни git reset
или же git rebase
действительно жизнеспособны, так как они меняют историю, что плохо с веткой, которая уже была сдвинута.
Я также пытался git merge
с различными стратегиями, но ни одна из них не отменяет локальные изменения, т. е. если бы я добавил файл, объединение могло бы привести в соответствие другие файлы, но у меня все еще будет тот файл, которого нет у исходного потока.
Я мог бы просто создать новую ветку вне апстрима, но мне бы очень хотелось, чтобы с точки зрения истории ревизий были применены все изменения, чтобы взять мою ветку и снова сделать ее идентичной апстриму, чтобы я мог безопасно продвинуть это изменение без истории Есть ли такая команда или серия команд?
8 ответов
Вы можете объединить свою ветку вверх по течению с вашей dev
ветка, с настраиваемым драйвером слияния "keepTheirs":
См " git merge -s theirs
"Необходимо - но я знаю, что его не существует ".
В твоем случае только один .gitattributes
потребуется, и keepTheirs
сценарий как:
mv -f $3 $2
exit 0
git merge --strategy=theirs
Симуляция № 1
Показывается как слияние, с восходящим потоком в качестве первого родителя.
Cascabel упоминает (в комментариях) merge -s ours
путем слияния вашей работы в восходящем потоке (или во временной ветви, начиная с восходящего), а затем перемотки вашей ветки вперед в результате этого слияния:
git checkout -b tmp origin/upstream
git merge -s ours downstream # ignoring all changes from downstream
git checkout downstream
git merge tmp # fast-forward to tmp HEAD
git branch -D tmp # deleting tmp
Преимущество заключается в том, чтобы записать предшествующего предшественника в качестве первого родителя, так что объединение означает "поглотить эту устаревшую ветку темы", а не "уничтожить эту ветку темы и заменить ее на восходящую".
(Изменить 2011):
Этот рабочий процесс был описан в этом сообщении в блоге OP:
Почему я хочу это снова?
Пока мое репо не имело ничего общего с публичной версией, все было в порядке, но с тех пор я бы хотел иметь возможность объединяться в WIP с другими членами команды и сторонними участниками, я хочу убедиться, что мои публичные ветки Надежность для других пользователей: отводить и извлекать данные, т. е. больше не нужно перезагружать и сбрасывать то, что я отправил в удаленное резервное копирование, поскольку теперь оно доступно на GitHub и общедоступно.
Так что это оставляет меня с тем, как я должен действовать.
99% времени моя копия попадает в основной апстрим, поэтому я хочу работать над своим мастером и большую часть времени продвигаться в апстрим.
Но время от времени, что я имею вwip
будет признан недействительным из-за того, что входит в апстрим, и я оставлю некоторую часть моегоwip
,
В этот момент я хочу вернуть моего мастера обратно в синхронизацию с восходящим потоком, но не уничтожать какие-либо точки коммитов на моем публично отправленном мастере. Т.е. я хочу слияние с апстримом, которое заканчивается набором изменений, делающим мою копию идентичной апстриму.
И вот чтоgit merge --strategy=theirs
следует сделать.
git merge --strategy=theirs
Симуляция № 2
Показывается как слияние с нашим первым родителем.
(предложено jcwenger)
git checkout -b tmp upstream
git merge -s ours thebranch # ignoring all changes from downstream
git checkout downstream
git merge --squash tmp # apply changes from tmp but not as merge.
git rev-parse upstream > .git/MERGE_HEAD #record upstream 2nd merge head
git commit -m "rebaselined thebranch from upstream" # make the commit.
git branch -D tmp # deleting tmp
git merge --strategy=theirs
Симуляция № 3
В этом блоге упоминается:
git merge -s ours ref-to-be-merged
git diff --binary ref-to-be-merged | git apply -R --index
git commit -F .git/COMMIT_EDITMSG --amend
иногда вы действительно хотите сделать это, и не потому, что у вас есть "дерьмо" в вашей истории, а, возможно, потому, что вы хотите изменить базовую линию для разработки в общедоступном репозитории, где следует избегать перебазирования.
git merge --strategy=theirs
Симуляция № 4
(то же сообщение в блоге)
В качестве альтернативы, если вы хотите обеспечить быструю переадресацию локальных восходящих веток, потенциальный компромисс заключается в том, чтобы работать с пониманием того, что для sid / unstable ветвь восходящего потока может время от времени сбрасываться / перебазироваться (основываясь на событиях, которые в конечном итоге отсутствуют). вашего контроля на стороне проекта вверх по течению).
Это не имеет большого значения, и работа с этим предположением означает, что легко поддерживать локальную ветвь восходящего потока в состоянии, когда требуется только быстрое обновление.
git branch -m upstream-unstable upstream-unstable-save
git branch upstream-unstable upstream-remote/master
git merge -s ours upstream-unstable
git diff --binary ref-to-be-merged | git apply -R --index --exclude="debian/*"
git commit -F .git/COMMIT_EDITMSG --amend
git merge --strategy=theirs
Симуляция № 5
(предложено Бараком А. Перлмуттером):
git checkout MINE
git merge --no-commit -s ours HERS
git rm -rf .
git checkout HERS -- .
git checkout MINE -- debian # or whatever, as appropriate
git gui # edit commit message & click commit button
git merge --strategy=theirs
Симуляция № 6
(предложено тем же Майклом Гебетсройтером):
Майкл Гебетсройтер вмешался, заявив, что я "обманываю";) и дал другое решение с сантехническими командами более низкого уровня:
(это не было бы git, если бы это было невозможно с командами git only, все в git с diff/patch/apply не является реальным решением;).
# get the contents of another branch
git read-tree -u --reset <ID>
# selectivly merge subdirectories
# e.g superseed upstream source with that from another branch
git merge -s ours --no-commit other_upstream
git read-tree --reset -u other_upstream # or use --prefix=foo/
git checkout HEAD -- debian/
git checkout HEAD -- .gitignore
git commit -m 'superseed upstream source' -a
Вы можете сделать это довольно легко сейчас:
$ git fetch origin
$ git merge origin/master -s recursive -Xtheirs
Это синхронизирует локальное репо с источником и сохраняет историю.
Для меня это звучит так:
$ git reset --hard origin/master
Если нет изменений в push upstream, и вы просто хотите, чтобы ветка upstream была вашей текущей веткой, это будет сделано. Делать это локально не вредно, но вы потеряете все локальные изменения **, которые не были переданы мастеру.
** На самом деле изменения все еще в силе, если вы их зафиксировали локально, так как коммиты все еще будут в вашем git reflog
обычно не менее 30 дней.
Еще одна симуляция для git merge -s theirs ref-to-be-merged
:
git merge --no-ff -s ours ref-to-be-merged # enforce a merge commit; content is still wrong
git reset --hard HEAD^2; git reset --soft HEAD@{1} # fix the content
git commit --amend
Альтернативой двойному сбросу будет применение обратного патча:
git diff --binary ref-to-be-merged | git apply -R --index
Есть также способ с небольшой помощью сантехнических команд - ИМХО самый простой. Скажем, вы хотите подражать "их" для случая с 2 ветками:
head1=$(git show --pretty=format:"%H" -s foo)
head2=$(git show --pretty=format:"%H" -s bar)
tree=$(git show --pretty=format:"%T" -s bar)
newhead=$(git commit-tree $tree -p $head1 -p $head2 <<<"merge commit message")
git reset --hard $newhead
Это объединяет произвольное количество головок (2 в приведенном выше примере), используя дерево одной из них (bar в примере выше, предоставляя "свое" дерево), игнорируя любые проблемы diff / file (commit-tree - команда низкого уровня, поэтому не заботится о тех). Обратите внимание, что голова может быть только 1 (так что эквивалент вишни с "их").
Обратите внимание, что родительский заголовок указан первым, может влиять на некоторые вещи (см., Например, --first-parent команды git-log) - так что имейте это в виду.
Вместо git-show можно использовать все остальное, способное выводить хэши деревьев и коммитов - все, что используется для анализа (cat-file, rev-list,...). Вы можете следить за всем с помощью git commit --amend для интерактивного украшения сообщения коммита.
Тяжело, но, черт возьми, что может пойти не так?
- Посмотрите на ветку X, которую вы хотите выглядеть как Y
- cp -r.git / tmp
- Проверьте филиал Y
- rm -rf.git && cp -r /tmp/.git.
- Фиксируйте и продвигайте любую разницу
- СДЕЛАННЫЙ.
Перейдите к удаленной ветке upstream и выполните git merge
со стратегией слияния, установленной на ours
,
git checkout origin/master
git merge dev --strategy=ours
git commit ...
git push
Вся история все еще будет присутствовать, но у вас будет дополнительный коммит слияния. Здесь важно начать с версии, в которой вы хотите быть, и объединить ours
с веткой GitHub на самом деле в.
Используйте git reset BACKWARDS!
Вы можете сделать ветку похожей на любой другой коммит с git reset
, но вы должны сделать это в обход.
Сделать ветку на коммите <old>
выглядеть как коммит <new>
, ты можешь сделать
git reset --hard <new>
чтобы сделать <new>
содержимое рабочего дерева.
Тогда делай
git reset --mixed <old>
изменить ветвь обратно на исходный коммит, но оставив рабочее дерево в <new>
состояние
Затем вы можете добавить и зафиксировать изменения, чтобы ваша ветка точно соответствовала содержимому <new>
совершить.
Это нелогично, чтобы перейти от <old>
государство к <new>
вам нужно сделать git reset
от <new>
в <old>
, Однако с возможностью --mixed
рабочее дерево осталось в <new>
и указатель ветви установлен в <old>
, так что когда изменения зафиксированы, ветка выглядит так, как мы хотим.
Предупреждение
Не упускайте из виду ваши коммиты, например, забудьте, что <old>
когда делает git reset --hard <new>
,
Я следовал за этими ролями:
Извлечение источника, жесткий сброс из ветви, затем рекурсивный из их, а затем принудительный толчок к ветви
НА СВОЙ РИСК
git fetch origin
git reset --hard origin/<branch>
git merge origin/<branch> -s recursive -Xtheirs
git push -f <remote> <branch>