تخطَّ إلى المحتوى

١١ يوليو ٢٠٢٦ · آخر تحديث: ١٩ أكتوبر ٢٠٢٦

حل خطأ failed to push some refs في Git بالعربي

شارك المقال:

تدفع عملك بثقة فيصدّك Git:

 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:user/repo.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref.

الرسالة مخيفة الشكل، لكن الكلمة بين القوسين تخبرك بالسبب كاملًا. كل الرسائل في هذا المقال أعدنا إنتاجها في مستودعات تجريبية على Git 2.43.

اقرأ ما بين القوسين أولًا

الرسالةماذا حدثالحل
[rejected] (fetch first)الريموت فيه commits لم تنزّلها أصلًاgit pull ثم git push
[rejected] (non-fast-forward)نزّلتها بـ fetch، لكن فرعك لا يحتويهاgit pull ثم git push
[rejected] (stale info)--force-with-lease رفضت: الريموت تغيّر منذ آخر fetchgit fetch وراجع، ثم ادمج
[rejected] (would clobber existing tag)tag بنفس الاسم يشير لـ commit آخرقرّر أي tag هو الصحيح (أدناه)
[remote rejected] (... hook declined)الخادم رفض بقاعدة عندهفرع جديد وpull request
[remote rejected] (branch is currently checked out)تدفع لمستودع عادي (غير bare) فرعه مفتوحادفع لفرع آخر أو لمستودع bare

الفرق المهم: [rejected] يعني أن Git عندك رفض قبل الإرسال لأن التاريخين لا يتطابقان، و[remote rejected] يعني أن الخادم استلم ثم رفض بقاعدة عنده. الأول يُحل بالدمج، والثاني لا يحله الدمج ولا القوة.

السبب الأشهر: الريموت تقدّم عليك

تاريخ الفرع سلسلة commits. عندما تدفع، يشترط Git أن يكون تاريخك امتدادًا مباشرًا لتاريخ الريموت (fast-forward). إن احتوى الريموت commit ليس في سلسلتك (زميل دفع قبلك، أو تعديل من واجهة GitHub، أو دفعتَ من جهاز آخر)، يصبح دفعك فرعًا موازيًا يرفضه Git حتى تدمج، حتى لا يضيع ذلك الـ commit.

الحل الآمن: يصلح في أغلب الحالات

git pull --rebase   # ينزّل عمل الريموت ويعيد وضع commits الخاصة بك فوقه
git push

أو بالدمج العادي إن كنت تفضّله:

git pull --no-rebase    # commit دمج يجمع التاريخين
git push

إن ظهرت تعارضات أثناء ذلك فهي ليست كارثة: حلّها، ثم git add و git rebase --continue (أو git commit في حالة الدمج). دليلها الكامل: حل Merge Conflict خطوة بخطوة.

«You have divergent branches»: حين يرفض git pull نفسه

جرّبنا git pull بلا أي إعداد، وفرعانا مختلفان، فرفض Git الدمج نفسه:

hint: You have divergent branches and need to specify how to reconcile them.
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
fatal: Need to specify how to reconcile divergent branches.

منذ Git 2.27 لا يختار Git بنفسه بين الدمج وإعادة البناء. اختر مرة واحدة لكل مستودعاتك:

git config --global pull.rebase true    # تاريخ مستقيم (المفضّل لفرعك الشخصي)
# أو
git config --global pull.rebase false   # دمج عادي (أوضح للمبتدئ في فريق)

الفرق بين الطريقتين بالتفصيل في الفرق بين rebase وmerge، وبين fetch وpull في مقاله.

حالة «أول دفع» الشهيرة

أنشأت المستودع على GitHub مع README، وبدأت محليًا بـ git init؟ التاريخان منفصلان تمامًا لا يعرف أحدهما الآخر:

git pull origin main --allow-unrelated-histories
git push -u origin main

والوقاية بسيطة: عند ربط مشروع محلي موجود، أنشئ المستودع على GitHub فارغًا بلا README ولا رخصة. (إعداد الريموت نفسه في درس GitHub والريموت.)

القوة: متى، ولماذا، وبأي صيغة

أحيانًا تاريخك المحلي هو الصحيح فعلًا: بعد rebase أو commit --amend لفرعك الشخصي الذي دفعته سابقًا. هنا فقط:

# الصيغة الآمنة: تفشل إن تغيّر الريموت عما رأيته آخر مرة
git push --force-with-lease

# تجنّبها: تستبدل الريموت بشكل أعمى، حتى لو دفع أحدهم قبل ثانية
git push --force

(stale info): حين ترفض --force-with-lease

 ! [rejected]        main -> main (stale info)

جرّبناها: زميل دفع commit جديدًا، ونحن نعمل على نسخة قديمة من الريموت، ثم حاولنا --force-with-lease، فرفضت. هذا نجاحها لا فشلها: لو كانت --force لمحت عمل الزميل بصمت. الصحيح:

git fetch
git log --oneline HEAD..origin/main   # ما الذي دُفع ولم تره؟
git rebase origin/main                # ضع عملك فوقه
git push --force-with-lease           # الآن «تعرف» الريموت، فتنجح

ولا تتجاوز الرفض بـ --force أبدًا. وانتبه لفخ خفي: إن كان محررك يعمل fetch تلقائيًا في الخلفية، تظن --force-with-lease أنك «رأيت» عمل زميلك وأنت لم تره. أضف --force-if-includes (منذ Git 2.30) لتشترط أن يكون الريموت داخل تاريخك المحلي فعلًا:

git push --force-with-lease --force-if-includes

القاعدة الحمراء: لا قوة على فرع يعمل عليه غيرك أبدًا، ولا على main. القوة لفروعك الشخصية حصرًا.

[remote rejected]: الخادم هو من رفض

remote: policy: no direct pushes to main
 ! [remote rejected] main -> main (pre-receive hook declined)

هذا ما أنتجه hook تجريبي وضعناه على مستودع. على GitHub تظهر الحماية برسائل مثل GH006: Protected branch update failed و(protected branch hook declined) حين يكون الفرع محميًا: يمنع الدفع المباشر، أو يشترط مراجعة أو فحوصات.

اقرأ أسطر remote: فوق الرفض: فيها القاعدة بكلماتها. والحل ليس pull ولا --force (القوة تُرفض هي أيضًا):

git switch -c fix/login-message
git push -u origin fix/login-message   # ثم افتح pull request على GitHub

(درس pull requests على GitHub.)

(branch is currently checked out) حالة أخرى من النوع نفسه، تظهر حين تدفع إلى مجلد مشروع عادي على جهاز آخر أو على خادمك، لا إلى GitHub. Git يرفض تحديث فرع مفتوح هناك حتى لا يختلف عن ملفاته. ادفع إلى فرع آخر، أو اجعل المستودع البعيد bare (git init --bare).

would clobber existing tag: tag مختلف بنفس الاسم

 ! [rejected]        v1         -> v1  (would clobber existing tag)

ظهرت لنا مع git fetch --tags حين كان tag محلي v1 يشير لـ commit، والـ v1 على الريموت لـ commit آخر. Git يرفض استبدال tag بصمت لأن الـ tags يُفترض أنها ثابتة. إن كان الريموت هو الصحيح: git fetch --tags --force. وإن كانت نسختك المحلية خاطئة ولم تدفعها بعد: git tag -d v1 ثم git fetch --tags. ولا تعدّل tag منشورًا أصلًا؛ أنشئ v1.0.1 بدلًا منه.

قائمة تحقّق سريعة

  • اقرأ ما بين القوسين: fetch first/non-fast-forward/stale info/remote rejected.
  • متقدم عليك؟ git pull --rebase ثم git push.
  • divergent branches؟ اختر pull.rebase مرة واحدة بـ git config --global.
  • أول دفع لريبو أُنشئ بـ README؟ --allow-unrelated-histories.
  • قوة؟ فرعك الشخصي فقط، و--force-with-lease لا --force.
  • stale info؟ fetch وراجع وادمج، لا تتجاوزها.
  • remote rejected؟ فرع جديد وpull request.

الخلاصة

failed to push some refs ليس عطلًا بل بوابة أمان، والكلمة بين القوسين تخبرك أي بوابة. الريموت متقدم؟ ادمج ثم ادفع. --force-with-lease رفضت؟ أنقذتك من محو عمل غيرك. الخادم رفض؟ القاعدة عنده، فالطريق pull request.

أتقن الفروع والريموت في مسار Git بالعربي، وأكمل السلسلة مع حل fatal: not a git repository.

📚 مصادر رسمية للتعمّق: التوثيق الرسمي لـ Git

الأسئلة الشائعة

ما الفرق بين fetch first وnon-fast-forward؟

كلاهما يعني أن الريموت فيه commits ليست في فرعك. fetch first تعني أنك لم تنزّلها بعد أصلًا (لا يعرفها Git محليًا). non-fast-forward تعني أنك نزّلتها بـ fetch لكن فرعك ما زال لا يحتويها. الحل واحد: git pull ثم git push.

ماذا تعني rejected (stale info) مع --force-with-lease؟

أن الفرع على الريموت تغيّر منذ آخر مرة نزّلت فيها حالته، فرفضت --force-with-lease الدفع حتى لا تمحو عملًا لم تره. هذا هو المقصود منها تمامًا. نفّذ git fetch، وراجع الجديد بـ git log HEAD..origin/<branch>، وادمجه في عملك، ثم ادفع. لا تحوّلها إلى --force لتتجاوز الرفض.

ما معنى You have divergent branches and need to specify how to reconcile them؟

تظهر مع git pull حين يكون لك commits وللريموت commits أخرى. منذ Git 2.27 لا يختار Git بنفسه بين الدمج وإعادة البناء، فيطلب أن تحدد. للمبتدئ: git config --global pull.rebase false (دمج) أو true (rebase)، أو مرّر --rebase أو --no-rebase في الأمر نفسه.

ما معنى remote rejected؟

الخادم استلم الدفع ثم رفضه بقاعدة عنده: فرع محمي على GitHub أو GitLab (رسالة مثل protected branch hook declined)، أو pre-receive hook يفحص الرسائل أو الملفات. هذه ليست مشكلة تاريخ، فلا يحلها pull ولا force. ادفع إلى فرع جديد وافتح pull request، أو اقرأ سطر remote: الذي يشرح القاعدة.

متى يظهر هذا الخطأ رغم أني أعمل وحدي؟

أشهر حالة: أنشأت المستودع على GitHub مع ملف README أو رخصة، ثم حاولت أول دفع من مشروع محلي، فالريموت عنده commit البداية وأنت لا. أو عدّلت ملفًا من واجهة GitHub مباشرة ونسيت. والحل git pull ثم git push.

اقرأ أيضًا

تصفّح كل المقالات في المدوّنة، أو ابدأ التعلّم من المسارات و خرائط الطريق.