سؤال

لقد سألت من قبل عن كيفية الاسكواش يرتكب الأولين في مستودع جيت.

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

لذا، أفضّل أن أتحمل الألم مرة واحدة فقط، ثم أتمكن بعد ذلك من استخدام قاعدة إعادة الأساس التفاعلية القياسية إلى الأبد.

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

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

هل كانت مفيدة؟

المحلول

إجابة منتصف 2017

ربما يكون من الأفضل إنشاء التزام جديد فارغ تمامًا بدون أي آثار جانبية باستخدام سباكة Git مباشرة.القيام بذلك بهذه الطريقة يتجنب أي آثار جانبية:لا يجوز لمس نسخة العمل أو الفهرس، ولا توجد فروع مؤقتة للتنظيف، وما إلى ذلك.لذا:

  1. لإنشاء التزام، نحتاج إلى شجرة دليل لذلك، لذا نقوم بإنشاء دليل فارغ أولاً:

    tree=`git hash-object -wt tree --stdin < /dev/null`
    
  2. الآن يمكننا أن نلتف حول الالتزام:

    commit=`git commit-tree -m 'root commit' $tree`
    
  3. والآن يمكننا إعادة البناء على ذلك:

    git rebase --onto $commit --root master
    

وهذا كل شيء.يمكنك إعادة ترتيب هذا الأمر برمته في سطر واحد إذا كنت تعرف قوقعتك جيدًا بما فيه الكفاية.

(ملاحظة:في الممارسة العملية سأستخدمها الآن filter-branch.سيتم تعديل ذلك لاحقًا.)


الإجابة التاريخية (المشار إليها بإجابات أخرى)

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

# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .

# then you apply the same steps
git commit --allow-empty -m 'root commit'
git rebase --onto newroot --root master
git branch -d newroot

فويلا، لقد انتهيت master مع إعادة كتابة تاريخه ليشمل التزامًا جذرًا فارغًا.


ملحوظة:على الإصدارات القديمة من Git التي تفتقر إلى --orphan التبديل إلى checkout, ، أنت بحاجة إلى السباكة لإنشاء فرع فارغ:

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d

نصائح أخرى

ودمج أرسطو Pagaltzis وإجابات أوفي كلين-كونيغ وتعليق ريتشارد Bronosky ل.

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
# touch .gitignore && git add .gitignore # if necessary
git commit --allow-empty -m 'initial'
git rebase --onto newroot --root master
git branch -d newroot

و(فقط لوضع كل شيء في مكان واحد)

وأحب الإجابة أرسطو. ولكن وجدت أن لمستودع كبير (> 5000 يرتكب) تصفية فرع يعمل بشكل أفضل من rebase لعدة أسباب 1) أنها أسرع 2) أنها لا تتطلب التدخل البشري عندما يكون هناك صراع الدمج. 3) يمكن إعادة كتابة العلامات - المحافظة عليها. لاحظ أن أعمال تصفية فرع لأنه لا يوجد سؤال حول محتويات كل كرست - هو بالضبط نفس قبل هذا "rebase '

وبلدي الخطوات هي:

# first you need a new empty branch; let's call it `newroot`
git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d

# then you apply the same steps
git commit --allow-empty -m 'root commit'

# then use filter-branch to rebase everything on newroot
git filter-branch --parent-filter 'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat master

لاحظ أن الخيارات "--tag اسم مرشح القط يعني أن علامات سيتم إعادة كتابة للإشارة إلى يرتكب تم إنشاؤه حديثا.

وكنت قطعة من أرسطو والجواب كينت بنجاح:

# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .
git commit --allow-empty -m 'root commit'
git filter-branch --parent-filter \
'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat -- --all
# clean up
git checkout master
git branch -D newroot
# make sure your branches are OK first before this...
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d

وهذا من شأنه أيضا إعادة كتابة جميع فروع (وليس فقط master) بالإضافة إلى العلامات.

وgit rebase --root --onto $emptyrootcommit

وينبغي أن تفعل خدعة بسهولة

وحصلت متحمس وكتب "idempotent" نسخة من هذا السيناريو لطيفة ... وسيتم إدراج نفسه دائما فارغة ارتكابها، وإذا قمت بتشغيل مرتين، فإنه لا يغير بك ارتكاب التجزئة في كل مرة. لذلك، وهنا بلدي يأخذ على <القوي> بوابة-إدراج فارغة الجذر : ل

#!/bin/sh -ev
# idempotence achieved!
tmp_branch=__tmp_empty_root
git symbolic-ref HEAD refs/heads/$tmp_branch
git rm --cached -r . || true
git clean -f -d
touch -d '1970-01-01 UTC' .
GIT_COMMITTER_DATE='1970-01-01T00:00:00 +0000' git commit \
  --date='1970-01-01T00:00:00 +0000' --allow-empty -m 'initial'
git rebase --committer-date-is-author-date --onto $tmp_branch --root master
git branch -d $tmp_branch

هل يستحق تعقيد إضافي؟ ربما لا، ولكن أنا سوف تستخدم هذه واحدة.

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

git remote rm origin
git remote add --track master user@host:path/to/repo

حسنا، وهنا ما خطرت لي:

# Just setting variables on top for clarity.
# Set this to the path to your original repository.
ORIGINAL_REPO=/path/to/original/repository

# Create a new repository…
mkdir fun
cd fun
git init
# …and add an initial empty commit to it
git commit --allow-empty -m "The first evil."

# Add the original repository as a remote
git remote add previous $ORIGINAL_REPO
git fetch previous

# Get the hash for the first commit in the original repository
FIRST=`git log previous/master --pretty=format:%H  --reverse | head -1`
# Cherry-pick it
git cherry-pick $FIRST
# Then rebase the remainder of the original branch on top of the newly 
# cherry-picked, previously first commit, which is happily the second 
# on this branch, right after the empty one.
git rebase --onto master master previous/master

# rebase --onto leaves your head detached, I don't really know why)
# So now you overwrite your master branch with the newly rebased tree.
# You're now kinda done.
git branch -f master
git checkout master
# But do clean up: remove the remote, you don't need it anymore
git remote rm previous

أعتقد أن استخدام git replace و git filter-branch هو حل أفضل من استخدام git rebase:

  • أداء أفضل
  • أسهل وأقل خطورة (يمكنك التحقق من نتيجتك في كل خطوة والتراجع عما فعلته...)
  • العمل بشكل جيد مع فروع متعددة مع نتائج مضمونة

الفكرة من وراء ذلك هي:

  • قم بإنشاء التزام فارغ جديد في الماضي البعيد
  • استبدل التزام الجذر القديم بالتزام مشابه تمامًا باستثناء أنه تتم إضافة التزام الجذر الجديد كأصل
  • تحقق من أن كل شيء كما هو متوقع وتشغيله git filter-branch
  • مرة أخرى، تأكد من أن كل شيء على ما يرام وقم بتنظيف ملفات git التي لم تعد هناك حاجة إليها

فيما يلي برنامج نصي للخطوتين الأولتين:

#!/bin/bash
root_commit_sha=$(git rev-list --max-parents=0 HEAD)
git checkout --force --orphan new-root
find . -path ./.git -prune -o -exec rm -rf {} \; 2> /dev/null
git add -A
GIT_COMMITTER_DATE="2000-01-01T12:00:00" git commit --date==2000-01-01T12:00:00 --allow-empty -m "empty root commit"
new_root_commit_sha=$(git rev-parse HEAD)

echo "The commit '$new_root_commit_sha' will be added before existing root commit '$root_commit_sha'..."

parent="parent $new_root_commit_sha"
replacement_commit=$(
 git cat-file commit $root_commit_sha | sed "s/author/$parent\nauthor/" |
 git hash-object -t commit -w --stdin
) || return 3
git replace "$root_commit_sha" "$replacement_commit"

يمكنك تشغيل هذا البرنامج النصي دون مخاطرة (حتى لو كان إجراء نسخة احتياطية قبل القيام بالإجراء الذي لم تقم به من قبل يعد فكرة جيدة؛))، وإذا لم تكن النتيجة هي النتيجة المتوقعة، فما عليك سوى حذف الملفات التي تم إنشاؤها في المجلد .git/refs/replace وحاول مرة أخرى ؛)

بمجرد التحقق من أن حالة المستودع هي ما تتوقعه، قم بتشغيل الأمر التالي لتحديث السجل جميع الفروع:

git filter-branch -- --all

الآن، يجب أن تشاهد تاريخين، القديم والجديد (انظر المساعدة في filter-branch للمزيد من المعلومات).يمكنك مقارنة الاثنين والتحقق مرة أخرى مما إذا كان كل شيء على ما يرام.إذا كنت راضيًا، فاحذف الملفات التي لم تعد بحاجة إليها:

rm -rf ./.git/refs/original
rm -rf ./.git/refs/replace

يمكنك العودة إلى الخاص بك master فرع وحذف الفرع المؤقت:

git checkout master
git branch -D new-root

والآن يجب أن يتم كل شيء ;)

وهنا بسيطة أونيلينير والتي يمكن استخدامها لإضافة فارغة تلتزم في بداية مستودع، إذا كنت قد نسيت لخلق فارغة ارتكاب مباشرة بعد "الحرف الأول بوابة":

git rebase --root --onto $(git commit-tree -m 'Initial commit (empty)' 4b825dc642cb6eb9a060e54bf8d69288fbee4904)

هنا بلدي bash البرنامج النصي على أساس كينتإجابة مع التحسينات:

  • فإنه يتحقق من الفرع الأصلي، وليس فقط master, ، عند الانتهاء؛
  • حاولت تجنب الفرع المؤقت، ولكن git checkout --orphan يعمل فقط مع الفرع، وليس مع حالة الرأس المنفصل، لذلك يتم سحبه لفترة كافية لإجراء الالتزام الجذري الجديد ثم حذفه؛
  • يستخدم تجزئة التزام الجذر الجديد أثناء filter-branch (ترك كينت عنصرًا نائبًا هناك للاستبدال اليدوي)؛
  • ال filter-branch عملية إعادة كتابة فقط الفروع المحلية، وليس أجهزة التحكم عن بعد أيضا
  • يتم توحيد البيانات التعريفية للمؤلف والمرسل بحيث يكون الالتزام الجذري متطابقًا عبر المستودعات.

#!/bin/bash

# Save the current branch so we can check it out again later
INITIAL_BRANCH=`git symbolic-ref --short HEAD`
TEMP_BRANCH='newroot'

# Create a new temporary branch at a new root, and remove everything from the tree
git checkout --orphan "$TEMP_BRANCH"
git rm -rf .

# Commit this empty state with generic metadata that will not change - this should result in the same commit hash every time
export GIT_AUTHOR_NAME='nobody'
export GIT_AUTHOR_EMAIL='nobody@example.org'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
git commit --allow-empty -m 'empty root'
NEWROOT=`git rev-parse HEAD`

# Check out the commit we just made and delete the temporary branch
git checkout --detach "$NEWROOT"
git branch -D "$TEMP_BRANCH"

# Rewrite all the local branches to insert the new root commit, delete the 
# original/* branches left behind, and check out the rewritten initial branch
git filter-branch --parent-filter "sed \"s/^\$/-p $NEWROOT/\"" --tag-name-filter cat -- --branches
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git checkout "$INITIAL_BRANCH"

لتبديل التزام الجذر:

أولاً، قم بإنشاء الالتزام الذي تريده كأول.

ثانيًا، قم بتبديل ترتيب الالتزامات باستخدام:

جيت rebase -i --root

سيظهر محرر مع الالتزامات حتى التزام الجذر، مثل:

اختر 1234 رسالة الجذر القديمة

اختر 0294 التزامًا في المنتصف

اختر الالتزام 5678 الذي تريد وضعه في الجذر

يمكنك بعد ذلك وضع الالتزام الذي تريده أولاً، وذلك بوضعه في السطر الأول.في المثال:

اختر الالتزام 5678 الذي تريد وضعه في الجذر

اختر 1234 رسالة الجذر القديمة

اختر 0294 التزامًا في المنتصف

عند الخروج من المحرر، سيتغير ترتيب الالتزام.

ملاحظة:لتغيير استخدامات git للمحرر، قم بتشغيل:

تكوين جيت - المحرر الأساسي العالمي name_of_the_editor_program_you_want_to_use

وبعد الإجابة أرسطو Pagaltzis وغيرها ولكن باستخدام أوامر أكثر بساطة

zsh% git checkout --orphan empty     
Switched to a new branch 'empty'
zsh% git rm --cached -r .
zsh% git clean -fdx
zsh% git commit --allow-empty -m 'initial empty commit'
[empty (root-commit) 64ea894] initial empty commit
zsh% git checkout master
Switched to branch 'master'
zsh% git rebase empty
First, rewinding head to replay your work on top of it...
zsh% git branch -d empty 
Deleted branch empty (was 64ea894).

ملاحظة الريبو لا ينبغي أن تحتوي على أي تعديلات المحلية في انتظار أن ترتكب.
وملاحظة git checkout --orphan تعمل على إصدارات جديدة من بوابة، وأعتقد.
ملاحظة معظم git status الوقت يعطي تلميحات مفيدة.

بدء مستودع جديد.

وتعيين تاريخك إلى تاريخ البدء تريد.

هل كل شيء بالطريقة التي يرغبون كنت فعلت ذلك، وتعديل وقت النظام لتعكس عندما كنت تمنى كنت فعلت ذلك بهذه الطريقة. سحب الملفات من المخزون الموجودة حسب الحاجة لتجنب الكثير من الكتابة لا داعي لها.

وعندما نصل الى اليوم، مبادلة مستودعات وأنت القيام به.

إذا كنت مجرد مجنون (التي أنشئت) ولكن ذكي معقول (على الأرجح، لأن لديك ليكون لها قدر معين من الذكاء إلى التفكير في الأفكار المجنونة مثل هذا) سوف النصي العملية.

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

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

لماذا تجعل الأشياء البسيطة معقدة؟

لديك A-B-C وتريد A-B-Z-C.

  1. git rebase -i trunk (أو أي شيء قبل ب)
  2. قم بتغيير الاختيار للتحرير على السطر B
  3. قم بإجراء التغييرات: git add ..
  4. git commit (git commit --amend والتي سوف تقوم بتحرير B وليس إنشاء Z)

[يمكنك عمل أكبر عدد ممكن git commit كما تريد هنا لإدراج المزيد من الالتزامات.بالطبع، قد تواجه مشكلات في الخطوة 5، ولكن حل تعارض الدمج مع git هو مهارة يجب أن تمتلكها.إذا لم يكن الأمر كذلك، تدرب!]

  1. git rebase --continue

بسيطة، أليس كذلك؟

إذا فهمت git rebase, ، فإن إضافة الالتزام "الجذر" لا ينبغي أن يمثل مشكلة.

نلهو مع بوابة!

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