बैश स्क्रिप्ट का उपयोग करके सभी गिट शाखाओं के माध्यम से पुनरावृति कैसे करें


105

मैं कैसे बैश स्क्रिप्ट का उपयोग करके अपने भंडार में सभी स्थानीय शाखाओं के माध्यम से पुनरावृति कर सकता हूं। मुझे इसे पुन: व्यवस्थित करने की आवश्यकता है और शाखा और कुछ दूरस्थ शाखाओं के बीच कोई अंतर है। भूतपूर्व

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

मुझे ऊपर दिए गए कुछ करने की आवश्यकता है, लेकिन मैं जिस मुद्दे का सामना कर रहा हूं वह $ है (गिट शाखा) मुझे रिपॉजिटरी फ़ोल्डर के अंदर फ़ोल्डर के साथ-साथ रिपॉजिटरी में मौजूद शाखाओं को भी देता है।

क्या इस मुद्दे को हल करने का यह सही तरीका है? या इसे करने का एक और तरीका है?

धन्यवाद



1
@pihentagy यह जुड़े हुए प्रश्न से पहले लिखा गया था।
कोदमान

जैसे: -for b in "$(git branch)"; do git branch -D $b; done
अभि

जवाबों:


156

स्क्रिप्ट लिखते समय आपको git ब्रांच का उपयोग नहीं करना चाहिए । Git एक "प्लंबिंग" इंटरफ़ेस प्रदान करता है जो स्पष्ट रूप से स्क्रिप्टिंग में उपयोग के लिए डिज़ाइन किया गया है (सामान्य Git कमांड के कई वर्तमान और ऐतिहासिक कार्यान्वयन (ऐड, चेकआउट, मर्ज, आदि) इसी इंटरफ़ेस का उपयोग करते हैं)।

आप जो प्लंबिंग कमांड चाहते हैं वह प्रत्येक के लिए git है :

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

नोट: आपको remotes/दूरस्थ रेफरी पर उपसर्ग की आवश्यकता नहीं है जब तक कि आपके पास अन्य रेफ्स न origin/masterहों जो रेफ नाम खोज पथ में कई स्थानों से मेल खाते हैं (देखें "ए प्रतीकात्मक रेफरी नाम ...") गिट-रेव-पार्स के निर्दिष्ट संशोधन अनुभाग में। (१) )। आप explictly अस्पष्टता से बचने की कोशिश कर रहे हैं, तो पूर्ण रेफरी नाम के साथ जाना: 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

आप इस आउटपुट को sh में पाइप कर सकते हैं ।

यदि आपको शेल कोड बनाने का विचार पसंद नहीं है, तो आप थोड़ी मजबूती * छोड़ सकते हैं और यह कर सकते हैं:

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)तत्वों को जोड़ने के लिए)।


39
गंभीरता से। ऐसा करने का एक सरल तरीका नहीं है?
जिम फेल

4
लेकिन क्या होगा अगर मैं git ब्रांच के फ़िल्टरिंग विकल्पों में से किसी एक का उपयोग करना चाहता हूं, जैसे --merged, मुझे git ब्रांच में लॉजिक को डुप्लिकेट करना होगा? ऐसा करने के लिए एक बेहतर तरीका होना चाहिए।
थायने

3
सरल संस्करण:git for-each-ref refs/heads | cut -d/ -f3-
wid

4
@ डेविड: या, बस,git for-each-ref refs/heads --format='%(refname)'
जॉन गीत्ज़ेन

5
@Thayne: इस सवाल पुराना है, लेकिन Git लोगों अंत में समस्या को संबोधित किया है: for-each-refअब की तरह सभी शाखा चयनकर्ताओं का समर्थन करता है --mergedऔर git branchऔर git tagअब वास्तव में के संदर्भ में लागू किया जाता है git for-each-refसूची से मौजूद मामलों के लिए, जो अपने आप में कम से कम। (नई शाखाओं और टैग बनाना, का हिस्सा नहीं है, और नहीं होना चाहिए for-each-ref।)
torek

50

ऐसा इसलिए है क्योंकि git branchतारांकन चिह्न के साथ वर्तमान शाखा को चिह्नित करता है, जैसे:

$ git branch
* master
  mybranch
$ 

इसलिए $(git branch)उदाहरण के लिए * master mybranch, और फिर *मौजूदा निर्देशिका में फ़ाइलों की सूची में फैलता है।

पहली जगह में तारांकन चिह्न नहीं छापने के लिए मुझे कोई स्पष्ट विकल्प नहीं दिखता है; लेकिन आप इसे काट सकते हैं:

$(git branch | cut -c 3-)

4
यदि आप दोहरे उद्धरण चिह्नों में घिरे हैं, तो आप तारांकन को विस्तारित करने से रोक सकते हैं - हालांकि आप अभी भी इसे आउटपुट से निकालना चाहते हैं। किसी भी बिंदु से एक तार को हटाने का एक अधिक मजबूत तरीका होगा $(git branch | sed -e s/\\*//g)
निक

2
अच्छा, मैं वास्तव में अपने 3-समाधान की तरह ।
आंद्रेई-निकुला पेट्रे

1
थोड़ा सरल $(git branch | sed 's/^..//')
सेड

5
थोड़ा सरल tr संस्करण:$(git branch | tr -d " *")
ccpizza

13

बैश बिलिन, इसके mapfileलिए बनाया गया है

सभी गिट शाखाएँ: git branch --all --format='%(refname:short)'

सभी स्थानीय गिट शाखाएँ: git branch --format='%(refname:short)'

सभी दूरस्थ गिट शाखाएं: git branch --remotes --format='%(refname:short)'

सभी गिट शाखाओं के माध्यम से पुनरावृति: 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 )

ओपी की विशिष्ट स्थिति के लिए:

#!/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}"

स्रोत: बिना तार के सभी स्थानीय गिट शाखाओं की सूची बनाएं


5

मैं इसे उदाहरण के लिए iterate:

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;

4

मैं सुझाव दूंगा कि $(git branch|grep -o "[0-9A-Za-z]\+")यदि आपकी स्थानीय शाखाओं का नाम अंक, az, और / या AZ अक्षर से ही दिया जाए


4

स्वीकृत उत्तर सही है और वास्तव में इसका उपयोग किया जाना चाहिए, लेकिन बैश में समस्या को हल करना यह समझने में एक महान अभ्यास है कि गोले कैसे काम करते हैं। अतिरिक्त पाठ हेरफेर किए बिना बैश का उपयोग करने की चाल, यह सुनिश्चित करने के लिए है कि गिट शाखा का आउटपुट शेल द्वारा निष्पादित होने वाली कमांड के हिस्से के रूप में कभी भी विस्तारित न हो। यह शेल विस्तार के फ़ाइल नाम विस्तार (चरण 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

बैश केस कंस्ट्रक्शन और पैरामीटर प्रतिस्थापन दोनों में तारांकन बैकस्लैश के साथ बचाए जाने की आवश्यकता होती है ताकि पैटर्न मिलान पात्रों के रूप में उन्हें खोल की व्याख्या करने से रोका जा सके। रिक्त स्थान बच गए हैं (टोकन को रोकने के लिए) क्योंकि आप सचमुच '*' से मेल खा रहे हैं।


4

मेरी राय में याद करने के लिए सबसे आसान विकल्प:

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

आउटपुट:

bamboo
develop
master

Grep -o विकल्प (--ऑन-मैचिंग) आउटपुट को केवल इनपुट के मिलान भागों तक सीमित करता है।

चूंकि न तो स्थान और न ही * Git शाखा नामों में मान्य हैं, यह अतिरिक्त वर्णों के बिना शाखाओं की सूची लौटाता है।

संपादित करें: यदि आप 'अलग सिर' स्थिति में हैं , तो आपको वर्तमान प्रविष्टि को फ़िल्टर करना होगा:

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


3

मैंने क्या किया, जो आपके प्रश्न पर लागू हुआ (और ccpizza उल्लेख से प्रेरित tr):

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

(मैं उपयोग करता हूं, जबकि मैं बहुत कुछ करता हूं। जबकि कुछ विशेष चीजों के लिए आप निश्चित रूप से एक इंगित चर नाम का उपयोग करना चाहते हैं ["शाखा", उदाहरण के लिए], ज्यादातर समय मैं केवल इनपुट की प्रत्येक पंक्ति के साथ कुछ करने से संबंधित हूं । 'शाखा' के बजाय 'लाइन' यहाँ पुन: प्रयोज्य / मांसपेशियों की स्मृति / दक्षता के लिए एक संकेत है।)


1

@ फिन के जवाब से धन्यवाद (धन्यवाद!), निम्नलिखित आपको बिना किसी इंटरव्यू के शेल स्क्रिप्ट बनाए बिना शाखाओं पर पुनरावृति करने देगा। यह काफी मजबूत है, जब तक कि शाखा के नाम में कोई नयापन नहीं है :)

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

जबकि लूप एक सबशेल में चलता है, जो आमतौर पर तब तक ठीक रहता है जब तक आप शेल वेरिएबल सेट नहीं करते हैं जिसे आप वर्तमान शेल में एक्सेस करना चाहते हैं। उस स्थिति में आप पाइप को उलटने के लिए प्रक्रिया प्रतिस्थापन का उपयोग करते हैं:

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

1

यदि आप इस राज्य में हैं:

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

यह मुझे कम जटिल समाधान दिखता है +1
Abhi

इसने मेरे लिए भी काम किया:for b in "$(git branch)"; do git branch -D $b; done
अभि

1

इसे सरल रखें

बैश स्क्रिप्ट का उपयोग करके लूप में शाखा नाम प्राप्त करने का सरल तरीका।

#!/bin/bash

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

आउटपुट:

master
other

1

Googlian का उत्तर है, लेकिन बिना उपयोग किए

git for-each-ref --format='%(refname:lstrip=-1)' refs/heads/

1
यह नामांकित शाखा नामों के लिए काम नहीं करता है, जैसे कि उन शाखाओं में जो उनमें स्लैश हैं। इसका मतलब यह है कि निर्भरबोट द्वारा बनाई गई शाखाएं, जो "dependabot / npm_and_yarn / typecript-3.9.5" जैसी कुछ दिखती हैं, इसके बजाय "टाइपस्क्रिप्ट-3.9.5" दिखाई देंगी।
इकोब्रोडी

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

यह git प्लंबिंग कमांड का उपयोग करता है , जिसे स्क्रिप्टिंग के लिए डिज़ाइन किया गया है। यह सरल और मानक भी है।

संदर्भ: गिट का बैश पूरा

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.