git - вернуть ветку, чтобы она выглядела как мастер?

У меня есть две ветки develop и master. В разработке много коммитов, которых еще нет в master. Хотя мне нужно, чтобы ветка develop выглядела точно так же, как master. Чтобы сохранить все изменения, внесенные в develop, я создам новую ветку из develop, чтобы все эти изменения не были потеряны.

Но после «копирования» develop, как я могу безопасно сбросить или вернуться к виду master?

Я видел это: Git: сбросить/вернуть всю ветку в другой штат филиалов?

Итак, чтобы сбросить, я могу сделать:

git checkout develop
git reset --hard master

Но проблема в том, что ветвь develop уже отправлена ​​на удаленный сервер, а другие уже вытащили develop.

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

Таким образом, история коммитов на develop выглядит примерно так (самый верхний означает последний коммит по дате):

commit hotfix2 - in both develop and master
some other commits that are only in develop
commit hotfix1 - in both develop and master
some commits that are only in develop
all commits that came when develop was created from master

person Andrius    schedule 30.06.2016    source источник
comment
Итак, что вы хотите сделать, это отменить все коммиты, которые были сделаны только в процессе разработки? (например, отменить «некоторые коммиты» и «некоторые другие коммиты» из вашей истории)   -  person poke    schedule 30.06.2016
comment
@poke Ну да, но есть ли способ проще, чем просмотреть все коммиты и выбрать те, которые мне нужно отменить? Это всего лишь пример, в реальном случае есть много коммитов, которые нужно отменить, и есть промежуточные коммиты, которые нужно сохранить. С reset это легко, я могу просто указать ветку, и она будет сброшена, чтобы выглядеть точно так же, как master.   -  person Andrius    schedule 30.06.2016


Ответы (2)


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

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

Вы также можете использовать флаг --no-commit или -n, чтобы не автоматически создавать фиксацию. Это позволяет вам объединять несколько команд git revert -n <commit> в цепочку друг за другом, не создавая откатную фиксацию для каждой. Затем, когда вы закончите выбирать все коммиты или диапазоны коммитов, которые хотите отменить, вы можете сделать один коммит, который объединяет их все.

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

Вместо этого мы начинаем новую ветку с master (которая затем имеет точно такое же содержимое) и сбрасываем эту ветку, чтобы вместо этого она основывалась на develop. Таким образом, мы сохраняем состояние рабочего каталога от master, но вместо этого фиксация будет следовать за develop. После этого мы можем перемотать вперед develop этот коммит:

# checkout a new branch off master
git checkout -b new-develop master

# make a soft reset to develop
git reset --soft develop

# commit the changes
git commit

# get back to develop and fast forward
git checkout develop
git merge --ff-only new-develop
git branch -d new-develop

Это приведет к тому же результату, что и при объединении git revert -n со всеми коммитами, исключительными для develop. Есть несколько других способов достичь этого состояния, но это действительно самый простой.

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

Итак, предполагая, что история изначально выглядит так:

                       master
                         ↓
* ------------ h1 ------ h2
 \              \         \
  * -- * -- * -- * -- * -- *
                           ↑
                         develop

Вы хотели бы превратить его в это:

                                master
                                  ↓
* ------------ h1 ------ h2 ----- M
 \              \         \      /  ↖
  * -- * -- * -- * -- * -- * -- F   develop

F — это исправление, которое мы создали выше. Это предполагает, что вы хотите объединить develop в master (git merge develop в то время как master), а затем перемотать вперед develop (git merge master в то время как develop), чтобы начать разработку заново с этой точки. Конечно, вы можете сделать это и в другом направлении, если вам так больше нравится.

В качестве альтернативы мы также можем выполнить это слияние M и исправление F за один шаг. Вы бы эффективно объединили develop в master и объединили все таким образом, чтобы получить содержимое master. Это будет выглядеть так:

                                master
                                  ↓
* ------------ h1 ------ h2 ---- FM
 \              \         \      /  ↖
  * -- * -- * -- * -- * -- * ---/   develop

Вы можете добраться туда вручную следующим образом:

# since we merge into master, we start there
git checkout master

# start the merge, but don’t attempt to fast-forward and do not
# commit the merge automatically (since we want to change it)
git merge --no-ff --no-commit develop

# reset the index that was prepared during the merge
git reset

# now checkout the files from master and commit the merge
git checkout master -- .
git add .
git commit

И на самом деле, это настолько распространенный сценарий, что git merge поставляется со стратегией слияния, которая делает именно это. Таким образом, вместо вышеописанного мы можем просто использовать стратегию слияния ours и отбрасывать все из ветки, которую мы объединяем в текущую:

git checkout master
git merge -s ours develop
person poke    schedule 30.06.2016
comment
Хм, я попробовал это, но, кажется, это не отменяет develop. Он отменяет только те файлы, которые были изменены как в master, так и в develop. Но есть много файлов, которые добавляются только в develop, но не в master. А те сохраняются. Поэтому, когда я заканчиваю проверять develop, а затем, когда я объединяю develop в master, все эти файлы фактически добавляются в master. Я что-то упустил здесь? Нужно ли мне просто вручную удалить все эти файлы и зафиксировать их? - person Andrius; 30.06.2016
comment
Какое решение вы пробовали? Самый последний (с использованием стратегии слияния ours) определенно должен работать; для других вам может понадобиться git reset перед git checkout master -- . (я забыл об этом). После слияния git diff <original-master> ничего не должно возвращать, а git diff <original-develop> должно показывать много изменений, которых больше нет в текущей ветке. - person poke; 30.06.2016
comment
Это странно, это должно сработать (при условии, что я правильно понимаю вашу историю). Может последний вариант попробовать? Если это не сработает, можете ли вы открыть комнату чата, и мы сможем продолжить там? - person poke; 30.06.2016
comment
Также третий вариант третий вариант с git объединяет наши, ничего не делать с develop, и когда я перехожу к master, я вижу коммиты от develop, но нет файлов от разработки. Разве это не должно быть сделано для develop вместо master? Мне кажется, что только первый вариант что-то делает для развития - person Andrius; 30.06.2016
comment
Да, последний вариант объединяет develop в master, отбрасывая любые изменения. Таким образом, вы объединяете историю, но сохраняете все изменения с develop (без сохранения содержимого). Это обновляет master, поэтому впоследствии вам нужно перемотать вперед develop до master (git merge master). - person poke; 30.06.2016
comment
Давайте продолжим обсуждение в чате. - person Andrius; 30.06.2016

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

git checkout origin/master .

Затем можно использовать git rm для удаления новых файлов, например

git rm newfile
person jerry.pepper    schedule 08.04.2018