команда git для создания одной ветви как другой - PullRequest
69 голосов
/ 06 февраля 2011

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

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

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

Ответы [ 9 ]

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

Вы можете объединить вашу ветку upstream с вашей веткой dev, с помощью настраиваемого драйвера слияния "keepTheirs" :
См. "" git merge -s theirs ", но я знаю этоне существует".
В вашем случае потребуется только один .gitattributes и скрипт keepTheirs, например:

mv -f $3 $2
exit 0

git merge --strategy=theirs Simulation #1

Отображается в виде слияния с восходящим потоком в качестве первого родителя.

Джефроми упоминает (в комментариях) 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 и public.

Так что мне остается действовать.
99% времени моя копия попадает в основной канал, так что я хочу работать с главным и большую часть времени работать в восходящем направлении.
Но время от времени то, что у меня есть в wip, становится недействительным из-за того, что входит в восходящий поток, и я откажусь от некоторой части моего wip.
В этот момент я хочу привести своимастер синхронизируется с апстримом, но не уничтожает какие-либо точки фиксации на моем общедоступном мастере.Т.е. я хочу слияние с апстримом, которое заканчивается набором изменений, делающим мою копию идентичной апстриму .
И это то, что git merge --strategy=theirs должен сделать.


git merge --strategy=theirs Simulation # 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 Simulation # 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 Simulation # 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 Simulation # 5

(предложено Barak A. Pearlmutter ):

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 Simulation # 6

(предложено тем же Michael Gebetsroither ):

вмешался Майкл Gebetsroither, утверждая, что я "обманывал";) и дал другое решение с низкоуровневыми сантехническими командами:

(было бы не 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
13 голосов
/ 06 февраля 2011

Для меня это звучит так, как будто тебе просто нужно сделать:

$ git reset --hard origin/master

Если нет изменений в push upstream, и вы просто хотите, чтобы восходящая ветвь была вашей текущей веткой, это будет сделано. Это не вредно делать это локально , но вы потеряете все локальные изменения **, которые не были переданы мастеру.

** На самом деле изменения все еще в силе, если вы их зафиксировали локально, так как коммиты все еще будут в вашем git reflog, обычно в течение как минимум 30 дней.

12 голосов
/ 16 ноября 2012

Вы можете сделать это довольно легко сейчас:

$ git fetch origin
$ git merge origin/master -s recursive -Xtheirs

Это синхронизирует локальное репо с источником и сохраняет историю.

5 голосов
/ 01 февраля 2014

Другая симуляция для 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
3 голосов
/ 06 сентября 2012

Есть также способ с небольшой помощью сантехнических команд - ИМХО самый простой.Скажем, вы хотите эмулировать "их" для 2 ветвей case:

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

Обратите внимание, что родительская головка, указанная первой, может влиять на некоторые вещи (см., Например, --first-parent of git-log команда) - так что имейте это в виду.

Вместо git-show можно использовать все остальное, способное выводить хеши дерева и фиксации - все, что используется для анализа (cat-file, rev-list)...).Вы можете следить за всем с помощью git commit --amend для интерактивного украшения сообщения коммита.

1 голос
/ 31 октября 2018

Используйте git reset НАЗАД!

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

1 голос
/ 06 марта 2017

Тяжело, но, черт возьми, что может пойти не так?

  • Посмотрите на ветку X, которую вы хотите выглядеть как Y
  • cp -r .git / tmp
  • Проверить филиал Y
  • rm -rf .git && cp -r /tmp/.git.
  • Зафиксируйте и запишите любую разницу
  • DONE.
1 голос
/ 06 февраля 2011

перейдите на удаленную ветвь восходящего потока и выполните git merge со стратегией слияния, установленной на ours.

git checkout origin/master
git merge dev --strategy=ours
git commit ...
git push

Вся история все еще будет присутствовать, но у вас будет дополнительный коммит слияния. Здесь важно начать с той версии, в которой вы хотите быть, и объединить ours с веткой github, которая на самом деле находится в.

0 голосов
/ 12 февраля 2019

Я следовал за этими ролями:

Извлечение источника, полный сброс из ветви, затем рекурсивный из их, а затем принудительный переход к ветви

НА СВОЙ СТРАХ И РИСК

git fetch origin
git reset --hard origin/<branch>
git merge origin/<branch> -s recursive -Xtheirs
git push -f <remote> <branch>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...