كيفية التكرار من خلال جميع فروع GIT باستخدام نص Bash

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

  •  27-09-2019
  •  | 
  •  

سؤال

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

for branch in $(git branch); 
do
    git log --oneline $branch ^remotes/origin/master;
done

أحتاج إلى القيام بشيء مثل المعطى أعلاه ، لكن المشكلة التي أواجهها هي $ (GIT Branch) تعطيني المجلدات الموجودة داخل مجلد المستودع مع الفروع الموجودة في المستودع.

هل هذه هي الطريقة الصحيحة لحل هذه المشكلة؟ أم أن هناك طريقة أخرى للقيام بذلك؟

شكرًا لك

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

المحلول

يجب ألا تستخدم فرع git عند كتابة البرامج النصية. GIT يوفر أ واجهة "السباكة" تم تصميم هذا بشكل صريح للاستخدام في البرمجة النصية (العديد من التطبيقات الحالية والتاريخية لأوامر GIT العادية (إضافة ، الخروج ، دمج ، إلخ) ، استخدم هذه الواجهة نفسها).

أمر السباكة الذي تريده git for-each-ref:

git for-each-ref --shell \
  --format='git log --oneline %(refname) ^origin/master' \
  refs/heads/

ملاحظة: لا تحتاج إلى remotes/ بادئة على المرجع البعيد ما لم يكن لديك الحكام الأخرى التي تسبب origin/master لمطابقة أماكن متعددة في مسار البحث عن اسم المرجع (انظر "اسم المرجع الرمزي ..." في قسم المراجعات المحدد في GIT-REV-PARSE (1)). إذا كنت تحاول أن تتجنب الغموض ، فانتقل مع اسم المرجع الكامل: refs/remotes/origin/master.

سوف تحصل على الإخراج مثل هذا:

git log --oneline 'refs/heads/master' ^origin/master
git log --oneline 'refs/heads/other' ^origin/master
git log --oneline 'refs/heads/pu' ^origin/master

يمكنك أن تنشر هذا الإخراج في ش.

إذا كنت لا تحب فكرة توليد رمز shell ، فيمكنك التخلي عن القليل من المتانة* وافعل هذا:

for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
    git log --oneline "$branch" ^origin/master
done

* يجب أن تكون أسماء المرجع في مأمن من تقسيم كلمة الصدفة (انظر git-check-ref-format (1)). أنا شخصياً سألتزم بالإصدار السابق (رمز الصدفة المولد) ؛ أنا أكثر ثقة من أنه لا شيء غير مناسب يمكن أن يحدث معها.

منذ أن حددت سحق وهو يدعم المصفوفات ، يمكنك الحفاظ على السلامة وتجنب توليد شجاعة حلقتك:

branches=()
eval "$(git for-each-ref --shell --format='branches+=(%(refname))' refs/heads/)"
for branch in "${branches[@]}"; do
    # …
done

يمكنك أن تفعل شيئًا مشابهًا مع $@ إذا كنت لا تستخدم قذيفة تدعم المصفوفات (set -- لتهيئة و set -- "$@" %(refname) لإضافة عناصر).

نصائح أخرى

هذا بسبب git branch يصادف الفرع الحالي مع النجمة ، على سبيل المثال:

$ git branch
* master
  mybranch
$ 

لذا $(git branch) يتوسع إلى مثل * master mybranch, ثم * يتوسع إلى قائمة الملفات في الدليل الحالي.

لا أرى خيارًا واضحًا لعدم طباعة العلامة النجمية في المقام الأول ؛ لكن يمكنك تقطيعه:

$(git branch | cut -c 3-)

باش بنيت ، mapfile, ، تم تصميمه لهذا

جميع فروع Git: git branch --all --format='%(refname:short)'

جميع فروع GIT المحلية: git branch --format='%(refname:short)'

جميع فروع GIT عن بُعد: git branch --remotes --format='%(refname:short)'

تكرار من خلال جميع فروع Git: mapfile -t -C my_callback -c 1 < <( get_branches )

مثال:

my_callback () {
  INDEX=${1}
  BRANCH=${2}
  echo "${INDEX} ${BRANCH}"
}
get_branches () {
  git branch --all --format='%(refname:short)'
}
# mapfile -t -C my_callback -c 1 BRANCHES < <( get_branches ) # if you want the branches that were sent to mapfile in a new array as well
# echo "${BRANCHES[@]}"
mapfile -t -C my_callback -c 1 < <( get_branches )

لموقف OP الخاص:

#!/usr/bin/env bash


_map () {
  ARRAY=${1?}
  CALLBACK=${2?}
  mapfile -t -C "${CALLBACK}" -c 1 <<< "${ARRAY[@]}"
}


get_history_differences () {
  REF1=${1?}
  REF2=${2?}
  shift
  shift
  git log --oneline "${REF1}" ^"${REF2}" "${@}"
}


has_different_history () {
  REF1=${1?}
  REF2=${2?}
  HIST_DIFF=$( get_history_differences "${REF1}" "${REF2}" )
  return $( test -n "${HIST_DIFF}" )
}


print_different_branches () {
  read -r -a ARGS <<< "${@}"
  LOCAL=${ARGS[-1]?}
  for REMOTE in "${SOME_REMOTE_BRANCHES[@]}"; do
    if has_different_history "${LOCAL}" "${REMOTE}"; then
      # { echo; echo; get_history_differences "${LOCAL}" "${REMOTE}" --color=always; } # show differences
      echo local branch "${LOCAL}" is different than remote branch "${REMOTE}";
    fi
  done
}


get_local_branches () {
  git branch --format='%(refname:short)'
}


get_different_branches () {
  _map "$( get_local_branches )" print_different_branches
}


# read -r -a SOME_REMOTE_BRANCHES <<< "${@}" # use this instead for command line input
declare -a SOME_REMOTE_BRANCHES
SOME_REMOTE_BRANCHES=( origin/master remotes/origin/another-branch another-remote/another-interesting-branch )
DIFFERENT_BRANCHES=$( get_different_branches )

echo "${DIFFERENT_BRANCHES}"

مصدر: اذكر جميع فروع GIT المحلية بدون نجمة نجمية

أنا أكرر كما هو على سبيل المثال:

for BRANCH in `git branch --list|sed 's/\*//g'`;
  do 
    git checkout $BRANCH
    git fetch
    git branch --set-upstream-to=origin/$BRANCH $BRANCH
  done
git checkout master;

أود أن أقترح$(git branch|grep -o "[0-9A-Za-z]\+") إذا تم تسمية فروعك المحلية بأرقام ، و/أو أحرف AZ و/أو AZ فقط

الإجابة المقبولة صحيحة ويجب أن تكون النهج المستخدم حقًا ، ولكن حل المشكلة في باش هو تمرين رائع في فهم كيفية عمل القذائف. تتمثل الحيلة في القيام بذلك باستخدام Bash دون إجراء معالجة نصية إضافية ، وهي ضمان عدم توسيع إخراج فرع GIT كجزء من أمر يتم تنفيذه بواسطة shell. هذا يمنع النجمة من التوسع في توسيع اسم الملف (الخطوة 8) من توسيع شل (انظر http://tldp.org/ldp/bash-beginners-guide/html/sect_03_04.html)

استخدم باش في حين بناء مع أمر قراءة لتقطيع إخراج فرع GIT في خطوط. سيتم قراءة "*" كشخصية حرفية. استخدم بيان حالة لمطابقةه ، مع إيلاء اهتمام خاص لأنماط المطابقة.

git branch | while read line ; do                                                                                                        
    case $line in
        \*\ *) branch=${line#\*\ } ;;  # match the current branch
        *) branch=$line ;;             # match all the other branches
    esac
    git log --oneline $branch ^remotes/origin/master
done

العلامات النجمية في كل من باش قضية يجب الهروب من البناء وفي الاستبدال المعلمة مع الانزلاق الخلفي لمنع قذيفة تفسيرها على أنها شخصيات مطابقة للأنماط. كما يتم هروب المساحات (لمنع الرمز المميز) لأنك تتطابق حرفيًا "*".

أسهل خيار للتذكر في رأيي:

git branch | grep "[^* ]+" -Eo

انتاج:

bamboo
develop
master

يقيد خيار GREP's -O (-المطابقة فقط) الإخراج على الأجزاء المطابقة فقط من الإدخال.

نظرًا لأن المساحة ولا * صالحة في أسماء فروع GIT ، فإن هذا يعيد قائمة الفروع بدون الأحرف الإضافية.

تحرير: إذا كنت في حالة "رأس منفصل", ، ستحتاج إلى تصفية الإدخال الحالي:

git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE

ما انتهى بي الأمر ، تطبيقه على سؤالك (واستلهم من CCPIZZA ذكر tr):

git branch | tr -d ' *' | while IFS='' read -r line; do git log --oneline "$line" ^remotes/origin/master; done

(أنا أستخدم بينما الحلقات كثيرًا. بينما بالنسبة لأشياء معينة ، فأنت بالتأكيد تريد استخدام اسم متغير مدبب ["فرع" ، على سبيل المثال] ، في معظم الوقت أنا مهتم فقط بفعل شيء ما مع كل منهما خط من المدخلات. استخدام "الخط" هنا بدلاً من "الفرع" هو إشارة إلى قابلية إعادة الاستخدام/ذاكرة/كفاءة العضلات.)

تمتد من إجابة Finn (شكرًا لك!) ، سيتيح لك ما يلي التكرار على الفروع دون إنشاء برنامج نصي Shell المتداخل. إنه قوي بما فيه الكفاية ، طالما لم يكن هناك خطوط جديدة في اسم الفرع :)

git for-each-ref --format='%(refname)' refs/heads  | while read x ; do echo === $x === ; done

تعمل الحلقة في Subshell ، وهو أمر جيد عادةً ما لم تقم بتعيين متغيرات Shell التي تريد الوصول إليها في القشرة الحالية. في هذه الحالة ، تستخدم استبدال العملية لعكس الأنبوب:

while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads )

إذا كنت في هذه الحالة:

git branch -a

* master

  remotes/origin/HEAD -> origin/master

  remotes/origin/branch1

  remotes/origin/branch2

  remotes/origin/branch3

  remotes/origin/master

وتدير هذا الرمز:

git branch -a | grep remotes/origin/*

for BRANCH in `git branch -a | grep remotes/origin/*` ;

do
    A="$(cut -d'/' -f3 <<<"$BRANCH")"
    echo $A

done        

ستحصل على هذه النتيجة:

branch1

branch2

branch3

master

أبقيها بسيطة

الطريقة البسيطة للحصول على اسم الفرع في حلقة باستخدام نص Bash.

#!/bin/bash

for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
    echo "${branch/'refs/heads/'/''}" 
done

انتاج:

master
other

إجابة Googlian ، ولكن دون استخدام إلى عن على

git for-each-ref --format='%(refname:lstrip=-1)' refs/heads/
for branch in "$(git for-each-ref --format='%(refname:short)' refs/heads)"; do
    ...
done

هذا يستخدم Git السباكة الأوامر ، والتي تم تصميمها للبرمجة النصية. إنها أيضًا بسيطة ومعتادة.

المرجعي: الانتهاء من باش جيت

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