انقل الالتزام (الالتزامات) الأحدث إلى فرع جديد باستخدام Git

StackOverflow https://stackoverflow.com/questions/1628563

سؤال

أرغب في نقل الالتزامات العديدة الأخيرة التي التزمت بإتقانها إلى فرع جديد واستعادة السيد مرة أخرى قبل تنفيذ تلك الالتزامات.لسوء الحظ، جهاز Git-fu الخاص بي ليس قويًا بدرجة كافية حتى الآن، هل من مساعدة؟

أي.كيف يمكنني أن أذهب من هذا

master A - B - C - D - E

الى هذا؟

newbranch     C - D - E
             /
master A - B 
هل كانت مفيدة؟

المحلول

الانتقال إلى فرع جديد

تحذير: تعمل هذه الطريقة لأنك تقوم بإنشاء فرع جديد باستخدام الأمر الأول: 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 (Make sure you know how many commits you need to go back)
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

نصائح أخرى

لأولئك الذين يتساءلون لماذا يعمل (كما كنت في البداية):

وترغب في العودة إلى 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 ، فإنه لا يزال يشير إلى ارتكاب فعل أصلا.

على العموم...

الطريقة التي كشفت عنها sykora هي الخيار الأفضل في هذه الحالة.لكن في بعض الأحيان لا تكون هذه هي الطريقة الأسهل وليست طريقة عامة.لاستخدام طريقة عامة جيت الكرز اختيار:

لتحقيق ما يريده OP، فهي عملية مكونة من خطوتين:

الخطوة 1 - لاحظ الالتزامات التي تريدها من السيد على ملف newbranch

ينفذ

git checkout master
git log

لاحظ تجزئات (على سبيل المثال 3) التي تريدها newbranch.وهنا سأستخدم:
ج الالتزام: 9aa1233
د الإلتزام : 453ac3d
الالتزام ه: 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

جيت الكرز اختيار يطبق تلك الالتزامات الثلاثة على الفرع الجديد.

وطريقة أخرى للقيام بذلك، وذلك باستخدام فقط 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 إلى . هو خدعة لطيفة إلى معرفته.

معظم الإجابات السابقة خاطئة بشكل خطير!

لا تفعل هذا:

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

كما في المرة القادمة التي تقوم فيها بتشغيل git rebase (أو git pull --rebase) سيتم تجاهل تلك الالتزامات الثلاثة بصمت newbranch!(انظر الشرح أدناه)

بدلاً من ذلك قم بما يلي:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • أولاً، يتجاهل الالتزامات الثلاثة الأخيرة (--keep يشبه --hard, ، ولكنه أكثر أمانًا، لأنه يفشل بدلاً من التخلص من التغييرات غير الملتزم بها).
  • ثم يتفرع newbranch.
  • ثم يقوم باختيار تلك الالتزامات الثلاثة مرة أخرى newbranch.وبما أنه لم يعد يتم الرجوع إليها بواسطة فرع، فإنه يفعل ذلك باستخدام git's إعادة التسجيل: HEAD@{2} هو الالتزام بذلك HEAD تستخدم للإشارة إلى عمليتين مضت، أي.قبل أن 1.فحصت newbranch و 2.مستخدم git reset لتجاهل الالتزامات الثلاثة.

تحذير:يتم تمكين إعادة التسجيل بشكل افتراضي، ولكن إذا قمت بتعطيله يدويًا (على سبيل المثال،باستخدام مستودع git "المجرد"، لن تتمكن من استعادة الالتزامات الثلاثة بعد التشغيل 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 تجاهل الالتزامات الثلاثة بعد المثال الأول؟انه بسبب git rebase مع عدم وجود حجج تمكن --fork-point الخيار افتراضيًا، والذي يستخدم إعادة التسجيل المحلي لمحاولة أن يكون قويًا ضد الفرع الرئيسي الذي يتم دفعه بالقوة.

لنفترض أنك قمت بتقسيم الأصل/الرئيسي عندما كان يحتوي على الالتزامات M1 وM2 وM3، ثم قمت بتنفيذ ثلاثة التزامات بنفسك:

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

ولكن بعد ذلك يقوم شخص ما بإعادة كتابة التاريخ عن طريق دفع الأصل/الرئيسي لإزالة M2:

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

باستخدام reflog المحلي الخاص بك، 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

لأنهم يتركون إعادة التسجيل في الحالة الخاطئة.يرى جيت newbranch كما لو كان قد تفرع من الفرع الرئيسي في مراجعة تتضمن الالتزامات الثلاثة، ثم reset --hard يعيد كتابة سجل المنبع لإزالة الالتزامات، وذلك في المرة القادمة التي تقوم فيها بالتشغيل git rebase فهو يتجاهلها مثل أي التزام آخر تمت إزالته من المنبع.

ولكن في هذه الحالة بالذات نريد أن يتم اعتبار هذه الالتزامات الثلاثة جزءًا من فرع الموضوع.ولتحقيق ذلك، نحتاج إلى التخلص من المنبع في المراجعة السابقة التي لا تتضمن الالتزامات الثلاثة.هذا ما تفعله الحلول المقترحة، ومن ثم يترك كلاهما إعادة التسجيل في الحالة الصحيحة.

لمزيد من التفاصيل، راجع تعريف --fork-point في ال جيت rebase و قاعدة دمج git مستندات.

حل أبسط بكثير باستخدام git stash

إليك طريقة أبسط بكثير للالتزام بالفرع الخطأ.البدء بالفرع master الذي لديه ثلاثة ارتكابات خاطئة:

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

متى تستخدم هذا؟

  • إذا كان هدفك الأساسي هو التراجع master
  • تريد الاحتفاظ بتغييرات الملف
  • أنت لا تهتم بالرسائل المتعلقة بالالتزامات الخاطئة
  • أنت لم تدفع بعد
  • تريد أن يكون هذا سهل الحفظ
  • لا تريد تعقيدات مثل الفروع المؤقتة/الجديدة، وإيجاد ونسخ تجزئات الالتزام، وغيرها من المشاكل

ما يفعله هذا، عن طريق رقم السطر

  1. يلغي الالتزامات الثلاثة الأخيرة (ورسائلهم) لـ master, ، ومع ذلك يترك كافة ملفات العمل سليمة
  2. يخفي جميع تغييرات ملف العمل، مما يجعل ملف master شجرة العمل تساوي تمامًا حالة HEAD~3
  3. التبديل إلى فرع موجود newbranch
  4. يطبق التغييرات المخفية على دليل العمل الخاص بك ويمسح المخزن

يمكنك الآن استخدام git add و git commit كما تفعل عادة.سيتم إضافة كافة الالتزامات الجديدة إلى newbranch.

ما هذا لا يفعل

  • لا يترك فروعًا مؤقتة عشوائية تشوش شجرتك
  • لا يحافظ على الالتزامات الخاطئة ورسائل الالتزام، لذلك ستحتاج إلى إضافة رسالة التزام جديدة إلى هذا الالتزام الجديد

الأهداف

ذكر OP أن الهدف هو "استعادة السيطرة على ما قبل تنفيذ تلك الالتزامات" دون فقدان التغييرات وهذا الحل يفعل ذلك.

أفعل ذلك مرة واحدة على الأقل في الأسبوع عندما أقوم بالتزامات جديدة عن طريق الخطأ master بدلاً من develop.عادةً ما يكون لدي التزام واحد فقط بالتراجع في هذه الحالة باستخدام git reset HEAD^ على السطر 1 هي طريقة أبسط للتراجع عن التزام واحد فقط.

لا تفعل هذا إذا قمت بدفع التغييرات الرئيسية إلى المنبع

ربما قام شخص آخر بسحب هذه التغييرات.إذا كنت تقوم فقط بإعادة كتابة سيدك المحلي، فلن يكون هناك أي تأثير عند دفعه إلى أعلى، ولكن دفع التاريخ المعاد كتابته إلى المتعاونين يمكن أن يسبب الصداع.

وهذا لا "نقل" لهم بالمعنى التقني لكنه لا يملك نفس التأثير:

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)

لذلك دون إعادة كتابة التاريخ (أي إذا كنت قد دفعت بالفعل ولجان):

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

ويمكن بعد ذلك أن يدفع كل الفروع دون استخدام القوة!

وكان مجرد هذا الوضع:

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

وكنت أتوقع أن ارتكاب سأكون الرأس، ولكن ارتكاب L هو عليه الآن ...

لتأكد على الأرض على المكان الصحيح في تاريخها أسهل للعمل مع تجزئة ارتكاب

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

1) إنشاء فرع جديد، والتي تتحرك كل ما تبذلونه من التغييرات على new_branch.

git checkout -b new_branch

2) ثم انتقل إلى فرع القديمة.

git checkout master

و3) هل بوابة rebase

git rebase -i <short-hash-of-B-commit>

و4) ثم يحتوي على محرر فتحت الماضي 3 ارتكاب المعلومات.

...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...

و5) تغيير pick إلى drop في كل تلك يرتكب 3. ثم حفظ وإغلاق المحرر.

...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...

و6) والآن تتم إزالة 3 يرتكب مشاركة من فرع الحالي (master). الآن دفع فرع بقوة، مع علامة + قبل اسم الفرع.

git push origin +master

كيف يمكنني أن أذهب من هذا

A - B - C - D - E 
                |
                master

الى هذا؟

A - B - C - D - E 
    |           |
    master      newbranch

مع أمرين

  • فرع git -m master newbranch

إعطاء

A - B - C - D - E 
                |
                newbranch

و

  • بوابة فرع رئيسي ب

إعطاء

A - B - C - D - E
    |           |
    master      newbranch

يمكنك القيام بذلك من خلال 3 خطوات بسيطة استخدمتها.

1) قم بإنشاء فرع جديد حيث تريد الالتزام بالتحديث الأخير.

git branch <branch name>

2) ابحث عن معرف الالتزام الأخير للالتزام بالفرع الجديد.

git log

3) انسخ معرف الالتزام هذا ولاحظ أن قائمة الالتزام الأحدث موجودة في الأعلى.حتى تتمكن من العثور على الالتزام الخاص بك.تجد هذا أيضًا عبر الرسالة.

git cherry-pick d34bcef232f6c...

يمكنك أيضًا تقديم بعض نطاق معرف الالتزام.

git cherry-pick d34bcef...86d2aec

الآن انتهت مهمتك.إذا اخترت المعرف الصحيح والفرع الصحيح فسوف تنجح.لذلك قبل القيام بذلك كن حذرا.وإلا يمكن أن تحدث مشكلة أخرى.

الآن يمكنك دفع التعليمات البرمجية الخاصة بك

git push

إذا كنت بحاجة فقط إلى نقل كل ما تبذلونه من غير مدفوعة يلتزم أ فرع جديد، ثم تحتاج فقط إلى ،

  1. يخلق أ فرع جديد من الحالي :git branch new-branch-name

  2. يدفع لك فرع جديد: git push origin new-branch-name

  3. يرجع لك الفرع القديم (الحالي). إلى آخر حالة مدفوعة/مستقرة: git reset --hard origin/old-branch-name

بعض الناس لديهم أيضا غيرها upstreams بدلا من origin, ، يجب عليهم استخدام مناسب upstream

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top