Как отменить merge request gitlab
Перейти к содержимому

Как отменить merge request gitlab

  • автор:

How to remove merge request from GitLab server

I created a merge request on gitlab (local) server. Now whenever I click on the merge request, the request times out with error 500. Before that I used to get an error code 504 and I applied the change mentioned in this gitlab support topic. All I want to do is remove the merge request. Is there a manual way of doing this?

7,079 6 6 gold badges 36 36 silver badges 54 54 bronze badges
asked Aug 25, 2015 at 2:33
3,780 6 6 gold badges 26 26 silver badges 31 31 bronze badges

4 Answers 4

Web UI Option

Today I discovered a way to do this with the Web UI.

So for Merge Request 14

On the bottom Right you should see a red Delete button.

Gitlab Delete Merge Request Screen Shot

PowerShell Option

Invoke-RestMethod -Method Delete -Uri ‘https://gitlab.example.com/api/v4/projects/PROJECT_ID_GOES_HERE/merge_requests/14’ -Headers @

1 1 1 silver badge
answered Apr 11, 2018 at 15:46
Eric D. Johnson Eric D. Johnson
10.4k 10 10 gold badges 39 39 silver badges 47 47 bronze badges

FYI: The Delete button is only available if you have permission Owner. Unfortunately not documented yet: GitLab permissions — docs.gitlab.com/ee/user/permissions.html but indirect permission hint here: Delete a merge request — docs.gitlab.com/ee/api/…

May 17, 2018 at 9:00

Good clarity @DoctorRudolf .Yeah, if you’re only at Master or Developer permissions levels you can’t delete via the web UI.

May 23, 2018 at 12:28

Maintainer level also doesn’t seem to have this option. Then again I’m not the admin of the GitLab instance so it might have been deactivated by the owner/admin.

Apr 20, 2022 at 5:39

Yes, there is. I could not find a way to remove the merge request in the user interface, but you can simply delete it from the database.

(Please note, that I only tested this on gitlab CE 8.4.0-ce.0 on Ubuntu 14.04.3 LTS.. Other versions may have a different database structure)

At a command prompt, execute the following command (as root):

sudo -u gitlab-psql /opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -d gitlabhq_production 

This will bring up a PostgreSQL command terminal. Next, you’ll have to find the merge request you’d like to delete. Type the following at the PostgreSQL command terminal:

select id, title from merge_requests; 

You’ll get a list of merge request ids and titles. Find the one you’d like to delete and note the id

OK, let’s say you’ve found the merge request you’d like to delete and the id is 5 . You’re simply going to delete all the data associated with that merge request using the following SQL commands. (Substitute 5 in the commands below with your actual merge request id )

delete from merge_requests where from merge_request_diffs where merge_request_id = 5; delete from notes where noteable_type = 'MergeRequest' and noteable_id = 5; 

You can now exit out of the PostgreSQL command terminal by typing:

Your merge request should now be gone from the web interface.

7.8 Инструменты Git — Продвинутое слияние

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

Однако, иногда всё же будут возникать сложные конфликты. В отличие от других систем управления версиями, Git не пытается быть слишком умным при разрешении конфликтов слияния. Философия Git заключается в том, чтобы быть умным, когда слияние разрешается однозначно, но если возникает конфликт, он не пытается сумничать и разрешить его автоматически. Поэтому, если вы слишком долго откладываете слияние двух быстрорастущих веток, вы можете столкнуться с некоторыми проблемами.

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

Конфликты слияния

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

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

Давайте рассмотрим очень простой пример. Допустим, у нас есть файл с исходниками на Ruby, выводящими на экран строку ‘hello world’.

#! /usr/bin/env ruby def hello puts 'hello world' end hello()

В нашем репозитории, мы создадим новую ветку по имени whitespace и выполним замену всех окончаний строк в стиле Unix на окончания строк в стиле DOS. Фактически, изменения будут внесены в каждую строку, но изменятся только пробельные символы. Затем мы заменим строку «hello world» на «hello mundo».

$ git checkout -b whitespace Switched to a new branch 'whitespace' $ unix2dos hello.rb unix2dos: converting file hello.rb to DOS format . $ git commit -am 'Convert hello.rb to DOS' [whitespace 3270f76] Convert hello.rb to DOS 1 file changed, 7 insertions(+), 7 deletions(-) $ vim hello.rb $ git diff -b diff --git a/hello.rb b/hello.rb index ac51efd..e85207e 100755 --- a/hello.rb +++ b/hello.rb @@ -1,7 +1,7 @@ #! /usr/bin/env ruby def hello - puts 'hello world' + puts 'hello mundo'^M end hello() $ git commit -am 'Use Spanish instead of English' [whitespace 6d338d2] Use Spanish instead of English 1 file changed, 1 insertion(+), 1 deletion(-)

Теперь мы переключимся обратно на ветку master и добавим к функции некоторую документацию.

$ git checkout master Switched to branch 'master' $ vim hello.rb $ git diff diff --git a/hello.rb b/hello.rb index ac51efd..36c06c8 100755 --- a/hello.rb +++ b/hello.rb @@ -1,5 +1,6 @@ #! /usr/bin/env ruby +# prints out a greeting def hello puts 'hello world' end $ git commit -am 'Add comment documenting the function' [master bec6336] Add comment documenting the function 1 file changed, 1 insertion(+)

Теперь мы попытаемся слить в текущую ветку whitespace и в результате получим конфликты, так как изменились пробельные символы.

$ git merge whitespace Auto-merging hello.rb CONFLICT (content): Merge conflict in hello.rb Automatic merge failed; fix conflicts and then commit the result.
Прерывание слияния

В данный момент у нас есть несколько вариантов дальнейших действий. Во-первых, давайте рассмотрим как выйти из этой ситуации. Если вы, возможно, не были готовы к конфликтам и на самом деле не хотите связываться с ними, вы можете просто отменить попытку слияния, используя команду git merge —abort .

$ git status -sb ## master UU hello.rb $ git merge --abort $ git status -sb ## master

Эта команда пытается откатить ваше состояние до того, что было до запуска слияния. Завершиться неудачно она может только в случаях, если перед запуском слияния у вас были не припрятанные или не зафиксированные изменения в рабочем каталоге, во всех остальных случаях всё будет хорошо.

Если по каким-то причинам вы обнаружили себя в ужасном состоянии и хотите просто начать всё сначала, вы можете также выполнить git reset —hard HEAD (либо вместо HEAD указав то, куда вы хотите откатиться). Но помните, что это откатит все изменения в рабочем каталоге, поэтому удостоверьтесь, что никакие из них вам не нужны.

Игнорирование пробельных символов

В данном конкретном случае конфликты связаны с пробельными символами. Мы знаем это, так как это простой пример, но в реальных ситуациях это также легко определить при изучении конфликта, так как каждая строка в нём будет удалена и добавлена снова. По умолчанию Git считает все эти строки изменёнными и поэтому не может слить файлы.

Стратегии слияния, используемой по умолчанию, можно передать аргументы, и некоторые из них предназначены для соответствующей настройки игнорирования изменений пробельных символов. Если вы видите, что множество конфликтов слияния вызваны пробельными символами, то вы можете прервать слияние и запустить его снова, но на этот раз с опцией -Xignore-all-space или -Xignore-space-change . Первая опция игнорирует изменения в любом количестве существующих пробельных символов, вторая игнорирует вообще все изменения пробельных символов.

$ git merge -Xignore-space-change whitespace Auto-merging hello.rb Merge made by the 'recursive' strategy. hello.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)

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

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

Ручное слияние файлов

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

То что нам действительно нужно — это перед выполнением самого слияния прогнать сливаемый файл через программу dos2unix . Как мы будем делать это?

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

Получить эти три версии файла, на самом деле, довольно легко. Git хранит все эти версии в индексе в разных «состояниях», каждое из которых имеет ассоциированный с ним номер. Состояние 1 — это общий предок, состояние 2 — ваша версия и состояния 3 взято из MERGE_HEAD — версия, которую вы сливаете («их» версия).

Вы можете извлечь копию каждой из этих версий конфликтующего файла с помощью команды git show и специального синтаксиса.

$ git show :1:hello.rb > hello.common.rb $ git show :2:hello.rb > hello.ours.rb $ git show :3:hello.rb > hello.theirs.rb

Если вы хотите что-то более суровое, то можете также воспользоваться служебной командой ls-files -u для получения SHA-1 хешей для каждого из этих файлов.

$ git ls-files -u 100755 ac51efdc3df4f4fd328d1a02ad05331d8e2c9111 1 hello.rb 100755 36c06c8752c78d2aff89571132f3bf7841a7b5c3 2 hello.rb 100755 e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd 3 hello.rb

Выражение :1:hello.rb является просто сокращением для поиска такого SHA-1 хеша.

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

$ dos2unix hello.theirs.rb dos2unix: converting file hello.theirs.rb to Unix format . $ git merge-file -p \ hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb $ git diff -b diff --cc hello.rb index 36c06c8,e85207e..0000000 --- a/hello.rb +++ b/hello.rb @@@ -1,8 -1,7 +1,8 @@@ #! /usr/bin/env ruby +# prints out a greeting def hello - puts 'hello world' + puts 'hello mundo' end hello()

Теперь у нас есть корректно слитый файл. На самом деле, данный способ лучше, чем использование опции ignore-all-space , так как в его рамках вместо игнорирования изменений пробельных символов перед слиянием выполняется корректное исправление таких изменений. При слиянии с ignore-all-space мы в результате получим несколько строк с окончаниями в стиле DOS, то есть в одном файле смешаются разные стили окончания строк.

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

Чтобы сравнить результат слияния с тем, что было в вашей ветке до слияния, или другими словами увидеть, что привнесло данное слияние, вы можете выполнить git diff —ours

$ git diff --ours * Unmerged path hello.rb diff --git a/hello.rb b/hello.rb index 36c06c8..44d0a25 100755 --- a/hello.rb +++ b/hello.rb @@ -2,7 +2,7 @@ # prints out a greeting def hello - puts 'hello world' + puts 'hello mundo' end hello()

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

Если вы хотите узнать чем результат слияния отличается от сливаемой ветки, то можете выполнить команду git diff —theirs . В этом и следующем примере мы используем опцию -w для того, чтобы не учитывать изменения в пробельных символах, так как мы сравниваем результат с тем, что есть в Git, а не с нашим исправленным файлом hello.theirs.rb .

$ git diff --theirs -b * Unmerged path hello.rb diff --git a/hello.rb b/hello.rb index e85207e..44d0a25 100755 --- a/hello.rb +++ b/hello.rb @@ -1,5 +1,6 @@ #! /usr/bin/env ruby +# prints out a greeting def hello puts 'hello mundo' end

И, наконец, вы можете узнать как изменился файл по сравнению сразу с обеими ветками с помощью команды git diff —base .

$ git diff --base -b * Unmerged path hello.rb diff --git a/hello.rb b/hello.rb index ac51efd..44d0a25 100755 --- a/hello.rb +++ b/hello.rb @@ -1,7 +1,8 @@ #! /usr/bin/env ruby +# prints out a greeting def hello - puts 'hello world' + puts 'hello mundo' end hello()

В данный момент мы можем использовать команду git clean для того, чтобы удалить не нужные более дополнительные файлы, созданные нами для выполнения слияния.

$ git clean -f Removing hello.common.rb Removing hello.ours.rb Removing hello.theirs.rb
Использование команды checkout в конфликтах

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

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

$ git log --graph --oneline --decorate --all * f1270f7 (HEAD, master) Update README * 9af9d3b Create README * 694971d Update phrase to 'hola world' | * e3eb223 (mundo) Add more tests | * 7cff591 Create initial testing script | * c3ffff1 Change text to 'hello mundo' |/ * b7dcc89 Initial hello world code

У нас есть три уникальных коммита, которые присутствуют только в ветке master и три других, которые присутствуют в ветке mundo . Если мы попытаемся слить ветку mundo , то получим конфликт.

$ git merge mundo Auto-merging hello.rb CONFLICT (content): Merge conflict in hello.rb Automatic merge failed; fix conflicts and then commit the result.

Мы хотели бы увидеть в чем состоит данный конфликт. Если мы откроем конфликтующий файл, то увидим нечто подобное:

#! /usr/bin/env ruby def hello >>>>>> mundo end hello()

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

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

Полезным в данном случае инструментом является команда git checkout с опцией —conflict . Она заново выкачает файл и заменит маркеры конфликта. Это может быть полезно, если вы хотите восстановить маркеры конфликта и попробовать разрешить его снова.

В качестве значения опции —conflict вы можете указывать diff3 или merge (последнее значение используется по умолчанию). Если вы укажете diff3 , Git будет использовать немного другую версию маркеров конфликта — помимо «нашей» и «их» версий файлов будет также отображена «базовая» версия, и таким образом вы получите больше информации.

$ git checkout --conflict=diff3 hello.rb

После того, как вы выполните эту команду, файл будет выглядеть так:

#! /usr/bin/env ruby def hello >>>>>> theirs end hello()

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

$ git config --global merge.conflictstyle diff3

Команде git checkout также можно передать опции —ours и —theirs , которые позволяют действительно быстро выбрать одну из версий файлов, не выполняя слияния совсем.

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

История при слиянии

Другой полезный инструмент при разрешении конфликтов слияния — это команда git log . Она поможет вам получить информацию о том, что могло привести к возникновению конфликтов. Временами может быть очень полезным просмотреть историю, чтобы понять почему в двух ветках разработки изменялась одна и та же область кода.

Для получения полного списка всех уникальных коммитов, которые были сделаны в любой из сливаемых веток, мы можем использовать синтаксис «трёх точек», который мы изучили в Три точки.

$ git log --oneline --left-right HEAD. MERGE_HEAD < f1270f7 Update README < 9af9d3b Create README < 694971d Update phrase to 'hola world' >e3eb223 Add more tests > 7cff591 Create initial testing script > c3ffff1 Change text to 'hello mundo'

Это список всех шести коммитов, включённых в слияние, с указанием также ветки разработки, в которой находится каждый из коммитов.

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

$ git log --oneline --left-right --merge < 694971d Update phrase to 'hola world' >c3ffff1 Change text to 'hello mundo'

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

Комбинированный формат изменений

Так как Git добавляет в индекс все успешные результаты слияния, то при вызове git diff в состоянии конфликта слияния будет отображено только то, что сейчас конфликтует. Это может быть полезно, так как вы сможете увидеть какие ещё конфликты нужно разрешить.

Если вы выполните git diff сразу после конфликта слияния, то получите информацию в довольно своеобразном формате.

$ git diff diff --cc hello.rb index 0399cd5,59727f0..0000000 --- a/hello.rb +++ b/hello.rb @@@ -1,7 -1,7 +1,11 @@@ #! /usr/bin/env ruby def hello ++>>>>>> mundo end hello()

Такой формат называется «комбинированным» («Combined Diff»), для каждого различия в нём содержится два раздела с информацией. В первом разделе отображены различия строки (добавлена она или удалена) между «вашей» веткой и содержимым вашего рабочего каталога, а во втором разделе содержится то же самое, но между «их» веткой и рабочим каталогом.

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

Если мы разрешим конфликт и снова выполним команду git diff , то получим ту же информацию, но в немного более полезном представлении.

$ vim hello.rb $ git diff diff --cc hello.rb index 0399cd5,59727f0..0000000 --- a/hello.rb +++ b/hello.rb @@@ -1,7 -1,7 +1,7 @@@ #! /usr/bin/env ruby def hello - puts 'hola world' - puts 'hello mundo' ++ puts 'hola mundo' end hello()

В этом выводе указано, что строка «hola world» при слиянии присутствовала в «нашей» ветке, но отсутствовала в рабочей копии, строка «hello mundo» была в «их» ветке, но не в рабочей копии, и, наконец, «hola mundo» не была ни в одной из сливаемых веток, но сейчас присутствует в рабочей копии. Это бывает полезно просмотреть перед коммитом разрешения конфликта.

Такую же информацию вы можете получить и после выполнения слияния с помощью команды git log , узнав таким образом как был разрешён конфликт. Git выводит информацию в таком формате, если вы выполните git show для коммита слияния или вызовете команду git log -p с опцией —cc (без неё данная команда не показывает изменения для коммитов слияния).

$ git log --cc -p -1 commit 14f41939956d80b9e17bb8721354c33f8d5b5a79 Merge: f1270f7 e3eb223 Author: Scott Chacon Date: Fri Sep 19 18:14:49 2014 +0200 Merge branch 'mundo' Conflicts: hello.rb diff --cc hello.rb index 0399cd5,59727f0..e1d0799 --- a/hello.rb +++ b/hello.rb @@@ -1,7 -1,7 +1,7 @@@ #! /usr/bin/env ruby def hello - puts 'hola world' - puts 'hello mundo' ++ puts 'hola mundo' end hello()

Отмена слияний

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

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

Случайный коммит слияния

Рисунок 137. Случайный коммит слияния

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

Исправление ссылок

Если нежелаемый коммит слияния существует только в вашем локальном репозитории, то простейшее и лучшее решение состоит в перемещении веток так, чтобы они указывали туда куда вам нужно. В большинстве случаев, если вы после случайного git merge выполните команду git reset —hard HEAD~ , то указатели веток восстановятся так, что будут выглядеть следующим образом:

История после `git reset --hard HEAD~`

Рисунок 138. История после git reset —hard HEAD~

Мы рассматривали команду reset ранее в Раскрытие тайн reset, поэтому вам должно быть не сложно разобраться с тем, что здесь происходит. Здесь небольшое напоминание: reset —hard обычно выполняет три шага:

  1. Перемещает ветку, на которую указывает HEAD. В данном случае мы хотим переместить master туда, где она была до коммита слияния ( C6 ).
  2. Приводит индекс к такому же виду что и HEAD.
  3. Приводит рабочий каталог к такому же виду, что и индекс.

Недостаток этого подхода состоит в изменении истории, что может привести к проблемам в случае совместно используемого репозитория. Загляните в Опасности перемещения, чтобы узнать что именно может произойти; кратко говоря, если у других людей уже есть какие-то из изменяемых вами коммитов, вы должны отказаться от использования reset . Этот подход также не будет работать, если после слияния уже был сделан хотя бы один коммит; перемещение ссылки фактически приведёт к потере этих изменений.

Отмена коммита

Если перемещение указателей ветки вам не подходит, Git предоставляет возможность сделать новый коммит, который откатывает все изменения, сделанные в другом. Git называет эту операцию «восстановлением» («revert»), в данном примере вы можете вызвать её следующим образом:

$ git revert -m 1 HEAD [master b1d8379] Revert "Merge branch 'topic'"

Опция -m 1 указывает какой родитель является «основной веткой» и должен быть сохранен. Когда вы выполняете слияние в HEAD ( git merge topic ), новый коммит будет иметь двух родителей: первый из них HEAD ( C6 ), а второй — вершина ветки, которую сливают с текущей ( C4 ). В данном случае, мы хотим отменить все изменения, внесённые слиянием родителя #2 ( C4 ), и сохранить при этом всё содержимое из родителя #1 ( C6 ).

История с коммитом восстановления (отменой коммита слияния) выглядит следующим образом:

История после `git revert -m 1`

Рисунок 139. История после git revert -m 1

Новый коммит ^M имеет точно такое же содержимое как C6 , таким образом, начиная с неё всё выглядит так, как будто слияние никогда не выполнялось, за тем лишь исключением, что «теперь уже не слитые» коммиты всё также присутствуют в истории HEAD . Git придёт в замешательство, если вы вновь попытаетесь слить topic в ветку master :

$ git merge topic Already up-to-date.

В ветке topic нет ничего, что ещё недоступно из ветки master . Плохо, что в случае добавления новых наработок в topic , при повторении слияния Git добавит только те изменения, которые были сделаны после отмены слияния:

История с плохим слиянием

Рисунок 140. История с плохим слиянием

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

$ git revert ^M [master 09f0126] Revert "Revert "Merge branch 'topic'"" $ git merge topic

История после повторения отменённого слияния

Рисунок 141. История после повторения отменённого слияния

В этом примере, M и ^M отменены. В коммите ^^M , фактически, сливаются изменения из C3 и C4 , а в C8 — изменения из C7 , таким образом, ветка topic полностью слита.

Другие типы слияний

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

Выбор «нашей» или «их» версий

Во-первых, существует ещё один полезный приём, который мы можем использовать в обычном «рекурсивном» режиме слияния. Мы уже видели опции ignore-all-space и ignore-space-change , которые передаются с префиксом -X , но мы можем также попросить Git при возникновении конфликта использовать ту или иную версию файлов.

По умолчанию, когда Git при слиянии веток замечает конфликт, он добавляет в код маркеры конфликта, отмечает файл как конфликтующий и позволяет вам разрешить его. Если же вместо ручного разрешения конфликта вы хотите, чтобы Git просто использовал какую-то определённую версию файла, а другую игнорировал, то вы можете передать команде merge одну из двух опций -Xours или -Xtheirs .

В этом случае Git не будет добавлять маркеры конфликта. Все неконфликтующие изменения он сольёт, а для конфликтующих он целиком возьмёт ту версию, которую вы указали (это относится и к бинарным файлам).

Если мы вернёмся к примеру «hello world», который использовали раньше, то увидим, что попытка слияния в нашу ветку приведёт к конфликту.

$ git merge mundo Auto-merging hello.rb CONFLICT (content): Merge conflict in hello.rb Resolved 'hello.rb' using previous resolution. Automatic merge failed; fix conflicts and then commit the result.

Однако, если мы выполним слияние с опцией -Xours или -Xtheirs , конфликта не будет.

$ git merge -Xours mundo Auto-merging hello.rb Merge made by the 'recursive' strategy. hello.rb | 2 +- test.sh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 test.sh

В этом случае, вместо добавления в файл маркеров конфликта с «hello mundo» в качестве одной версии и с «hola world» в качестве другой, Git просто выберет «hola world». Однако, все другие неконфликтующие изменения будут слиты успешно.

Такая же опция может быть передана команде git merge-file , которую мы обсуждали ранее, то есть для слияния отдельных файлов можно использовать команду git merge-file —ours .

На случай если вам нужно нечто подобное, но вы хотите, чтобы Git даже не пытался сливать изменения из другой версии, существует более суровый вариант — стратегия слияния «ours». Важно отметить, что это не то же самое что опция «ours» рекурсивной стратегии слияния.

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

$ git merge -s ours mundo Merge made by the 'ours' strategy. $ git diff HEAD HEAD~ $

Вы можете видеть, что между веткой, в которой мы были, и результатом слияния нет никаких отличий.

Это часто бывает полезно, когда нужно заставить Git считать, что ветка уже слита, а реальное слияние отложить на потом. Для примера предположим, что вы создали ветку release и проделали в ней некоторую работу, которую когда-то впоследствии захотите слить обратно в master . Тем временем в master были сделаны некоторые исправления, которые необходимо перенести также в вашу ветку release . Вы можете слить ветку с исправлениями в release , а затем выполнить merge -s ours этой ветки в master (хотя исправления в ней уже присутствуют), так что позже, когда вы будете снова сливать ветку release , не возникнет конфликтов, связанных с этими исправлениями.

Слияние поддеревьев

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

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

Первым делом мы добавим в наш проект приложение Rack. Мы добавим Rack в наш собственный проект, как удалённый репозиторий, а затем выгрузим его в отдельную ветку.

$ git remote add rack_remote https://github.com/rack/rack $ git fetch rack_remote --no-tags warning: no common commits remote: Counting objects: 3184, done. remote: Compressing objects: 100% (1465/1465), done. remote: Total 3184 (delta 1952), reused 2770 (delta 1675) Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. Resolving deltas: 100% (1952/1952), done. From https://github.com/rack/rack * [new branch] build -> rack_remote/build * [new branch] master -> rack_remote/master * [new branch] rack-0.4 -> rack_remote/rack-0.4 * [new branch] rack-0.9 -> rack_remote/rack-0.9 $ git checkout -b rack_branch rack_remote/master Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. Switched to a new branch "rack_branch"

Таким образом, теперь у нас в ветке rack_branch находится основная ветка проекта Rack, а в ветке master — наш собственный проект. Если вы переключитесь сначала на одну ветку, а затем на другую, то увидите, что они имеют абсолютно разное содержимое:

$ ls AUTHORS KNOWN-ISSUES Rakefile contrib lib COPYING README bin example test $ git checkout master Switched to branch "master" $ ls README

Может показаться странным, но, на самом деле, ветки в вашем репозитории не обязаны быть ветками одного проекта. Это мало распространено, так как редко бывает полезным, но иметь ветки, имеющие абсолютно разные истории, довольно легко.

В данном примере, мы хотим выгрузить проект Rack в подкаталог нашего основного проекта. В Git мы можем выполнить это с помощью команды git read-tree . Вы узнаете больше о команде read-tree и её друзьях в главе Git изнутри, сейчас же вам достаточно знать, что она считывает содержимое некоторой ветки в ваш текущий индекс и рабочий каталог. Мы просто переключимся обратно на ветку master и выгрузим ветку rack_branch в подкаталог rack ветки master нашего основного проекта:

$ git read-tree --prefix=rack/ -u rack_branch

Когда мы будем выполнять коммит, он будет выглядеть так, как будто все файлы проекта Rack были добавлены в этот подкаталог — например, мы скопировали их из архива. Важно отметить, что слить изменения одной из веток в другую довольно легко. Таким образом, если проект Rack обновился, мы можем получить изменения из его репозитория просто переключившись на соответствующую ветку и выполнив операцию git pull :

$ git checkout rack_branch $ git pull

Затем мы можем слить эти изменения обратно в нашу ветку master .

Для того, чтобы получить изменения и заполнить сообщение коммита используйте параметр —squash , вместе с опцией -Xsubtree рекурсивной стратегии слияния. Вообще-то, по умолчанию используется именно рекурсивная стратегия слияния, но мы указали и её тоже для пущей ясности.

$ git checkout master $ git merge --squash -s recursive -Xsubtree=rack rack_branch Squash commit -- not updating HEAD Automatic merge went well; stopped before committing as requested

Все изменения из проекта Rack слиты и подготовлены для локального коммита. Вы также можете поступить наоборот — сделать изменения в подкаталоге rack вашей основной ветки и затем слить их в вашу ветку rack_branch , чтобы позже передать их ответственным за проекты или отправить их в вышестоящий репозиторий проекта Rack.

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

Другая небольшая странность состоит в том, что для получения различий между содержимым подкаталога rack и содержимого ветки rack_branch — для того, чтобы увидеть необходимо ли выполнять слияния между ними — вы не можете использовать обычную команду diff . Вместо этого следует выполнить команду git diff-tree , указав ветку, с которой производится сравнение:

$ git diff-tree -p rack_branch

Вот как выглядит процедура сравнения содержимого подкаталога rack с содержимым ветки master на сервере после последнего скачивания изменений:

$ git diff-tree -p rack_remote/master

Как отменить merge, не потеряв код?

Разработку вел в master. Потом сделал валидацию в ветке validation и gui в ветке gui. Нужно было выложить на гитхаб разными ветками это. А я смерджил в master и validation и gui и только после этого отправил на гитхаб. Можно как то исправить? Мне важно что бы именно на гитхабе они отображались как разные ветки. Поэтому если есть способ на самом сайте их разделить визуально, при этом на компьютере оставив слитыми, то такой способ мне тоже подойдет. Код в этих ветках меня устраивает, и при слиянии не было никаких конфликтов. Хочу разделить, только потому что люди, которые будут изучать проект на гитхабе должны четко понимать, где закончилась ветка master. Т.е. только небольшая часть проекта должна быть в мастер. А у меня получилось, что весь проект в master.

Отслеживать
задан 8 мая 2016 в 5:58
Александр Елизаров Александр Елизаров
2,788 2 2 золотых знака 17 17 серебряных знаков 36 36 бронзовых знаков

3 ответа 3

Сортировка: Сброс на вариант по умолчанию

Во-первых, потерять код в git не так-то легко. Если что, у вас всегда есть git reflog , чтобы восстановить потерянный коммит. А ещё всегда можно сделать бэкап текущей ветки с помощью git branch backup (имя произвольное).

Дальше, нужно откатить ветку master до коммита перед слиянием: git reset —hard commit-name . Подробнее: Как вернуться (откатиться) к более раннему коммиту?

Просто так запушить ветку не получится, т.к. на гитхабе теперь есть коммиты, которых в вашей ветке нет, поэтому не получится автоматический fast-forward merge. Поможет git push -f . Эта команда заменяет содержимое удаленной ветки.

Осталось запушить остальные ветки. Чтобы каждый раз не указывать локальную и удаленную ветку, можете однажды назначить локальной ветке удаленную upstream-ветку:

git push -u origin master git push -u origin validation git push -u origin gui 

Можно запушить все ветки сразу (вообще все в refs/heads/ ):

git push -u --all origin Branch master set up to track remote branch master from origin. Branch validation set up to track remote branch validation from origin. Branch gui set up to track remote branch gui from origin. 

Смерджил ветку с main, как отменить и вернуть как было?

1. Сделал pull request из ветки А в ветку main на сайте
2. Нажал кнопку merge и смерджил в ветку main
3. Сделал Close в pull request
4. Как отменить все действия?

  • Вопрос задан более года назад
  • 4165 просмотров

Комментировать
Решения вопроса 1

sergey-kuznetsov

Сергей Кузнецов @sergey-kuznetsov Куратор тега Git
Автоматизатор

646ff3e1c8abf820618810.png

В интерфейсе GitHub есть специальная кнопка для этого.

Ответ написан более года назад
Нравится 2 3 комментария

honor8

Если так? git reset —merge HEAD~
https://stackoverflow.com/questions/2389361

sergey-kuznetsov

Сергей Кузнецов @sergey-kuznetsov Куратор тега Git
h8nor, подразумевается что main защищена от удаления. Мы можем только добавлять коммиты.

honor8

Сергей Кузнецов, вы правы. Так не работает https://qna.habr.com/q/853893
Но такой запрос проходит для владельца удаленного репозитория

git push -f origin SHA1:main # remote: Bypassed rule violations for refs/heads/master: # remote: # remote: - Changes must be made through a pull request. # remote: # remote: - Cannot force-push to this branch # remote: # remote: - Cannot change this locked branch git reflog expire --expire=now --all && git gc --prune=now --aggressive # чистка мусора в (gitk --)reflog

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *