Питання Перемістіть останні дії (-и) до нової гілки з Git


Я хотів би перемістити останні кілька зобов'язань, я взяв участь у майстерні у новій гілці та повернувся до майстра, перш ніж ці зобов'язання були зроблені. На жаль, мій Git-fu ще недостатньо сильний, будь-яка допомога?

І.е. Як я можу піти від цього?

master A - B - C - D - E

до цього?

newbranch     C - D - E
             /
master A - B 

3762
2017-10-27 03:07


походження


Примітка. Я запитав зворотне питання тут - Benjol
можливий дублікат Переміщення здійснює від майстра до філії за допомогою git - Chris Moschini
eddmann.com/posts/... це працює - Sagar Naliyapara
Чи були тут коментарі очищені? Я запитую, тому що під час свого одноразового відвідування цього питання я завжди прокручуюсь за цим коментарем. - Tejas Kale


Відповіді:


Переїзд до нової гілки

УВАГА: Цей метод працює, оскільки ви створюєте нову гілку з першою командою: git branch newbranch. Якщо ви хочете рухатися, зобов'язуйтесь до існуюча філія перед виконанням ви повинні об'єднати свої зміни в існуючу гілку git reset --hard HEAD~3 (побачити Перехід до існуючої гілки нижче) Якщо ви не спочатку об'єднаєте свої зміни, вони будуть втрачені.

Якщо не існують інші обставини, це може бути легко здійснено шляхом розгалуження і відкат.

# Note: Any changes not committed will be lost.
git branch newbranch      # Create a new branch, saving the desired commits
git reset --hard HEAD~3   # Move master back by 3 commits (GONE from master)
git checkout newbranch    # Go to the new branch that still has the desired commits

Але не забудьте, скільки зобов'язується повернутися. Крім того, ви можете замість цього HEAD~3, просто введіть хеш коректування (або посилання, як походження / майстер) Ви хочете "повернути назад" на магістр (/ поточна) гілка, наприклад:

git reset --hard a1b2c3d4

* 1 Ви будете тільки бути "втративши", зобов'язує від головної гілки, але не хвилюйтеся, у вас будуть ті, хто зобов'язується в новійдільниці!

УВАГА: З Git версії 2.0 і пізніше, якщо пізніше git rebase нове відгалуження від оригіналу (master) філія, вам може знадобитися явний --no-fork-point варіант під час переробки, щоб уникнути втрати здійснюваних зобов'язань. Маючи branch.autosetuprebase always набір робить це більш імовірним. Побачити Відповідь Джона Меллора для подробиць.

Перехід до існуючої гілки

Якщо ви хочете перевести свої зобов'язання на існуюча філія, це виглядатиме так:

git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch

4795
2017-07-22 22:37



І зокрема, ні спробуйте повернутися далі, ніж точка, де ви нарешті штовхнули, передає в інший сховище, з якого хтось інший міг би витягнути. - Greg Hewgill
Цікаво, якщо ви можете пояснити ЧОМУ це працює. Для мене ви створюєте нову гілку, знімаєте 3 копії зі старої філії, на якій ви все ще перебуваєте, а потім перевірте філію, яку ви зробили. Отже, як зроблені вами видатки магічно з'являються у новій гілці? - Jonathan Dumaine
@ Jonathan Dumaine: Тому що я створив нову гілку, перш ніж вилучати зобов'язання зі старої гілки. Вони все ще там у новій гілці. - sykora
гілки в git - це просто маркери, які вказують на коректні дії в історії, клонування, створення чи видалення (окрім маркерів) нічого немає. - knittl
Також зверніть увагу: не робіть цього з необов'язковими змінами у вашій робочій копії! Це просто закусило мене! :( - Adam Tuttle


Для тих, хто задає питання, чому це працює (як я був спочатку):

Ви хочете повернутися до C, і перемістити D та E до нової гілки. Ось що спочатку виглядає:

A-B-C-D-E (HEAD)
        ↑
      master

Після git branch newBranch:

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

Після git reset --hard HEAD~2:

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

Оскільки філія є лише покажчиком, магістр вказав на останню фіксацію. Коли ти зробив newBranch, ви просто зробили новий покажчик на останній фіксації. Тоді використовуючи git reset ти перемістив магістр покажчик повертається до двох. Але так як ти не рухався newBranch, він все ще вказує на першочергове здійснення.


847
2018-02-07 16:58



Ви потребуєте - серйозно, тому що інакше git залишає зміни з команд скидання у робочому каталозі (або, принаймні, зробив для мене). - Andrew
Я також повинен був зробити a git push origin master --force щоб зміни відображалися в основному сховищі. - Dženan
Ця відповідь спричиняє втрату зобов'язань: наступного разу ти git rebase, 3 зобов'язання будуть мовчазно відкинуті з newbranch. Побачити моя відповідь для деталей та більш безпечних альтернатив. - John Mellor
@ Джон, це дурниця. Переміщення, не знаючи, що ви робите, призводить до втрати. Якщо ви втратили чинність, мені дуже шкода, але ця відповідь не втратила ваших зобов'язань. Зауважте, що origin/master не відображається на наведеній вище діаграмі. Якщо ви натиснули origin/master і тоді внесли зміни вище, впевнені, що все буде смішно. Але це "Доктор, це боляче, коли я роблю це" тип проблеми. І це не підходить для того, про що запитали оригінальне питання. Я пропоную вам написати своє власне запитання, щоб вивчити ваш сценарій, а не викрасти цю. - Ryan Lundy
@ Джон, у відповідь ви сказали: «Не робіть цього! git branch -t newbranch". Поверніться і прочитайте відповіді знову. Ніхто запропонував це зробити. - Ryan Lundy


В загальному...

Метод, відкритий сикорою, є найкращим варіантом у цьому випадку. Але іноді це не найпростіше, і це не загальний метод. Для загального використання використовуйте гітарний вишня:

Для досягнення того, що хоче OP, це двоступеневий процес:

Крок 1 - примітка, яку виконує майстер, який ви хочете на a newbranch

Виконати

git checkout master
git log

Зверніть увагу, що хеш (скажімо 3) зобов'язує вас newbranch. Тут я буду використовувати:
C commit: 9aa1233
D commit: 453ac3d
E commit: 612ecb3 

Примітка: Ви можете використовувати перші сім символів або   все робити хеш

Крок 2 - Покладіть їх на newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

АБО (на Git 1.7.2+, діапазони використання)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

гітарний вишня застосовує ті три зобов'язання до новоїпідряду.


343
2018-03-26 08:13



Чи не ОП хотіли рухатися від магістр до новачниця Якщо ви вишнете під час господаря, ви додасте б до основної гілки - додавши, що робить, що це вже було, насправді. І це не повертає ні головки відділення до Б. Або є щось тонкий і прохолодний, я не отримую? - RaveTheTadpole
Це дуже добре працює, якщо ви випадково вчинили неправильну гілку немайстра, коли потрібно було створити нову гілку. - julianc
+1 для корисного підходу в деяких ситуаціях. Це добре, якщо ви хочете лише витягнути свої власні зобов'язання (які переплітаються з іншими) у нову гілку. - Tyler V.
Краще відповісти. Таким чином, ви можете переміщати зобов'язання в будь-яку гілку. - skywinder
Чи важливе значення має порядок вибору вишні? - kon psych


Ще один спосіб зробити це, використовуючи лише 2 команди. Також зберігає вашу поточну робочу дерево без змін.

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

Стара версія - перш ніж я дізнався про це git branch -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

Бути в змозі push до . це хороший трюк знати.


252
2018-04-06 22:38



Поточний каталог. Я думаю, це буде працювати, лише якщо ви перебуваєте у верхньому каталозі. - aragaer
Місцевий поштовх викликає посмішку, але на віддзеркалення, як це відрізняється git branch -f тут - jthill
@GerardSexton . є поточним директором. git може натискати на REMOTES або GIT URL. path to local directory підтримується синтаксис Git-адрес. Див. Розділ GIT URLS в git help clone. - weakish
Я не знаю, чому це не оцінюється вище. Мертвий простий, і без малих, але потенційних небезпек перезарядки - важкий. - Godsmith
@ Годсміт. Здогадаюсь, люди віддають перевагу трьом простим командам для двох трохи більш неясних команд. Також найкращі голоси, отримані від відповіді, отримують більше джерел за своєю природою, які відображаються спочатку. - JS_Riddler


Більшість попередніх відповідей небезпечно неправильно!

Не роби цього:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

У наступний раз біжиш git rebase (або git pull --rebase) ці 3 обвинувачення будуть мовчазно відкинуті з newbranch! (див. пояснення нижче)

Замість цього:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • Спочатку вона відкидає 3 останніх зобов'язання (--keep це як --hard, але безпечніше, тому що помирає, а не викидає непотрібні зміни).
  • Потім він знімає newbranch.
  • Тоді вишня-вибирає тих, котрі повертаються на 3 newbranch. Оскільки вони більше не посилаються на гілку, це робиться за допомогою git's відрегулювати: HEAD@{2} це те, що робить це HEAD використовується для позначення 2 операцій назад, тобто перед тим, як ми 1. перевірили newbranch і 2. використовується git reset відмовитися від трьох зобов'язань.

Попередження: reflog активовано за умовчанням, але якщо ви його вручну вимкнули (наприклад, за допомогою "голого" сховища git), ви не зможете отримати 3 повернення після виконання git reset --keep HEAD~3.

Альтернативою, яка не залежить від рефлог, є:

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(якщо ви віддаєте перевагу, то можете написати @{-1} - раніше зафіксована галузь - замість oldbranch)


Технічні пояснення

Чому б це? git rebase відмовитися від виконання 3 після першого прикладу? Це тому, що git rebase без аргументів дозволяє --fork-point опція за замовчуванням, яка використовує локальний reflog, щоб спробувати бути надійним проти гілки, що перебуває у верхній частині, підштовхуючи силу.

Припустимо, що ви розгадали початковий / майстер, коли він містив зобов'язання M1, M2, M3, після чого три зробив себе самостійно:

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

але тоді хтось переписує історію силою, натискаючи походження / master, щоб видалити M2:

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

Використовуючи ваш локальний блог, git rebase може бачити, що ви вирили з більш раннього втілення походження / майстер гілки, а отже, що здійснення M2 і M3 не є дійсно частиною гілки вашого тексту. Звідси випливає з тієї причини, що з моменту вилучення M2 з гілки верхнього потоку ви більше не хочете, щоб це було у вашій гілці теми, як тільки відгалужує гілку теми:

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

Ця поведінка має сенс, і, як правило, це правильно робити при переробці.

Тому причиною неможливості наступних команд:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

це тому, що вони залишають reflog у неправильному стані. Гіт бачить newbranch як відкинувши гілку вище, після перегляду, яка включає в себе 3 зобов'язує, то reset --hard перезаписує історію поточного потоку, щоб видалити корективи, і тому наступного разу ви запустите git rebase він відкидає їх, як і будь-які інші вчинки, які були вилучені з висхідного потоку.

Але в цьому конкретному випадку ми хочемо, щоб ці 3 зобов'язувалися розглядатися як частина галузевої теми. Щоб досягти цього, ми повинні вимкнути верхній потік при попередній редакції, яка не включає 3 зобов'язання. Ось що мої запропоновані рішення роблять, отже вони обидва залишають reflog в правильному стані.

Для отримання додаткової інформації див. Визначення --fork-point в git rebase і git merge-baseдокументи


210
2017-10-19 14:12



Ця відповідь каже: "НЕ роби це!" над тим, що ніхто не пропонував робити. - Ryan Lundy
@ Кіральсас, -t ви маєте на увазі в git branch буває неявно, якщо у вас є git config --global branch.autosetuprebase always встановити Навіть якщо ви цього не зробите, я вже пояснив вам, що така сама проблема виникає, якщо ви встановлюєте відстеження після виконання цих команд, оскільки ОП, який, напевно, має намір зробити з урахуванням їх запитання. - John Mellor
@RockLee, так, загальним способом виправити подібні ситуації є створення нової гілки (newbranch2) з безпечної відправної точки, тоді вишня вибирати всі зобов'язання, які ви хочете зберегти (від поганої нової групи до нової спільноти2). Вишневий призведе до виконання нових хешей, тому ви зможете безпечно переробляти newbranch2 (і тепер можете видалити погану нову частину). - John Mellor
@ Вал, ви неправильно зрозуміли: git rebase розроблений таким чином, щоб бути надійним на виїзді з переосмисленням їх історії. На жаль, побічні ефекти цієї стійкості впливають на всіх, навіть якщо ні вони, ні їхні вищевказані люди ніколи не переписують історію. - John Mellor
Ви також можете використовувати --no-fork-point коли ти біжиш git rebase. - torek


Це не "переміщає" їх у технічному сенсі, але має такий же ефект:

A--B--C  (branch-foo)
 \    ^-- I wanted them here!
  \
   D--E--F--G  (branch-bar)
      ^--^--^-- Opps wrong branch!

While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)

A--B--C  (branch-foo)
 \
  \
   D-(E--F--G) detached
   ^-- (branch-bar)

Switch to branch-foo
$ git cherry-pick E..G

A--B--C--E'--F'--G' (branch-foo)
 \   E--F--G detached (This can be ignored)
  \ /
   D--H--I (branch-bar)

Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:

A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
 \
  \
   D--H--I--J--K--.... (branch-bar)

27
2018-01-21 16:10



Ви не можете використовувати rebase за ту ж річ? - Bergi
Так, ви могли б альтернативно використовувати rebase на відокремленій гілці в сценарії вище. - Sukima


Щоб зробити це без переписування історії (тобто, якщо ви вже натиснули на це):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

Обидві гілки можна толкати без сили!


19
2017-09-20 10:17



Але тоді ви повинні мати справу зі сценарієм зворотного зв'язку, який, залежно від вашої обставини, може бути набагато складнішим. Якщо ви повернете фіксацію у філію, Gіt все одно побачить ті зобов'язання, які відбулися, тому для того, щоб скасувати це, вам доведеться повернути назад. Це спалює декілька людей, особливо коли вони повертають злиття і намагаються злити галузь назад, лише щоб виявити, що Гіт вважає, що це вже злило цю гілку (що цілком справедливо). - Makoto
Ось чому я вишня-вибираю укладені в кінці, на нову гілку. Таким чином, гіт бачить їх як нові зобов'язання, які вирішують вашу проблему. - teh_senaus
Це більш небезпечно, ніж це здається першим, оскільки ви змінюєте стан історії сховища, не розуміючи наслідків цього стану. - Makoto
Я не слідкую вашим аргументам - точка цієї відповіді полягає в тому, що ви не змінюєте історію, просто додавайте нові зобов'язання (які фактично скасовують повторне внесення змін). Ці нові зобов'язання можуть бути висунуті та злиті як звичайно. - teh_senaus


Була така ситуація:

Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N

Я виконав:

git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch

Я сподівався, що вчинюся, що я буду головою, але зараз я зробив це ...

Щоб бути впевненим, що вийшли на потрібне місце в історії, легше працювати з хеш-кодом

git branch newbranch 
git reset --hard #########
git checkout newbranch

12
2018-05-01 07:50





Набагато простіше рішення

Якщо:

  1. Ваша основна мета - повернути назад master, і
  2. Ви хочете зберегти зміни, але особливо не хвилюйтеся індивідуальні зобов'язуються, і
  3. Ви ще не підштовхнули

Тоді наступне набагато простіше (починаючи з гілки master з помилковими зобов'язаннями):

git reset HEAD~3
git stash
git checkout newbranch
git stash pop

Що це робить:

  1. Скасовує останні три дії (і їхні повідомлення) на master, але залишає всі робочі файли недоторканими
  2. Призупиняє весь робочий файл, змінюючи його master робоче дерево, рівне HEAD ~ 3 стану.
  3. Перемикається на існуючу гілку newbranch
  4. Витягує файли, що були змінені, зі списку і в робочий каталог.

Тепер ви можете використовувати git add і git commit як це зазвичай буде. Усі зміни будуть здійснені в newbranch.

Що це не робить:

  • Це не залишає випадкових тимчасових гілок, що захаращують ваше дерево.
  • Він не зберігає помилкові вчинки та повідомлення. Вам потрібно буде додати нове повідомлення про помилку для цього нового зобов'язання. Проте ОП стверджував, що мета полягає в тому, щоб "повернути майстер назад до того, як ці зобов'язання були зроблені", не втрачаючи змін, і це рішення це робить.

Я роблю це щонайменше раз на тиждень, коли я випадково вношу нові зобов'язання master замість develop. Зазвичай я маю лише одну згоду на відкат, в цьому випадку git reset HEAD^ працює на відкат лише одну фіксацію.


8
2018-06-01 05:40