簡體   English   中英

如何使用 bash 腳本遍歷所有 git 分支

[英]How to iterate through all git branches using bash script

如何使用 bash 腳本遍歷存儲庫中的所有本地分支。 我需要迭代並檢查分支和一些遠程分支之間是否有任何區別。 前任

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

我需要做上面給出的事情,但我面臨的問題是 $(git branch) 給了我存儲庫文件夾中的文件夾以及存儲庫中存在的分支。

這是解決此問題的正確方法嗎? 還是有其他方法可以做到這一點?

謝謝

編寫腳本時不應使用git branch Git 提供了一個明確設計用於腳本編寫的“管道”接口(普通 Git 命令(添加、簽出、合並等)的許多當前和歷史實現都使用相同的接口)。

你想要的管道命令是git for-each-ref

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

注意:您不需要remotes/前綴遙控器上的裁判,除非你有其他裁判這項事業origin/master ,以配合在裁判的名字搜索路徑的多個地方(見“A符號裁判的名字......。” 的指定修訂部分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

您可以將此輸出通過管道傳輸sh

如果您不喜歡生成 shell 代碼的想法,您可以放棄一些健壯性*並執行以下操作:

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

* Ref 名稱應該不受 shell 的分詞影響(請參閱git-check-ref-format(1) )。 我個人會堅持使用以前的版本(生成的 shell 代碼); 我更有信心,它不會發生任何不恰當的事情。

由於您指定了bash並且它支持數組,因此您可以保持安全並仍然避免生成循環的內臟:

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-)

bash 內置函數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 中解決問題是理解 shell 如何工作的一個很好的練習。 在不執行額外文本操作的情況下使用 bash 執行此操作的技巧是確保 git branch 的輸出永遠不會被擴展為要由 shell 執行的命令的一部分。 這可以防止星號在 shell 擴展的文件名擴展(第 8 步)中擴展(參見http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

使用帶有讀取命令的 bash while構造將 git 分支輸出分成幾行。 '*' 將作為文字字符讀入。 使用 case 語句進行匹配,特別注意匹配模式。

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

bash case構造和參數替換中的星號都需要用反斜杠轉義,以防止 shell 將它們解釋為模式匹配字符。 空格也會被轉義(以防止標記化),因為您實際上是在匹配 '* '。

我認為最容易記住的選項:

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

輸出:

bamboo
develop
master

Grep 的 -o 選項(--only-matching)將輸出限制為僅輸入的匹配部分。

由於空格和 * 在 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

(我經常使用 while 循環。雖然對於特定的事情,你肯定想使用一個帶尖的變量名 [“branch”,例如],但大多數時候我只關心對每一輸入做一些事情。使用這里的“line”而不是“branch”是對可重用性/肌肉記憶/效率的認可。)

如果您處於這種狀態:

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

谷歌的答案,但沒有用於

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 管道命令。 它也很簡單和標准。

參考: Git 的 Bash 完成

延續@finn 的回答(謝謝!),以下內容將讓您在不創建干預 shell 腳本的情況下遍歷分支。 只要分支名稱中沒有換行符,它就足夠強大:)

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

while 循環在子shell 中運行,這通常很好,除非您設置要在當前shell 中訪問的shell 變量。 在這種情況下,您可以使用進程替換來反轉管道:

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

簡單地迭代本地分支名稱的正確方法是使用for-each-ref over refs/heads/ 例如:

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

這適用於IFS的標准/默認值,因為 git 中的分支名稱中的空格是非法的。

列出本地存儲庫中的頭(分支)

git show-ref --heads

這將列出頭部類似

682e47c01dc8d0f4e4102f183190a48aaf34a3f0 refs/heads/main
....

所以如果你只對名字感興趣,你可以使用像sed這樣的東西來獲得你想要的輸出

git show-ref --heads | sed 's/.*refs\/heads\///'

遍歷分支

有了這個輸出,你可以輕松地遍歷它,比如使用 bash 循環、xargs、任何漂浮在你船上的東西

for SHA in $(git show-ref --heads | awk '{ print $1 }'); do
 echo "magic! $SHA"
done
  • git show-ref --heads按照上面的git show-ref --heads獲取分支
  • awk '{ print $1 }'獲取 SHA
  • echo "magic! $SHA" <- 這是你施展魔法的地方
#/bin/bash
for branch in $(git branch -r); 
do
    echo $branch
done

當然,理論上應該使用 Git 在編寫腳本時確實具有的特殊接口。 通常你想要一些更簡單的東西——對於單行來說很方便。 有些東西不會促使你記住像 git for-each-ref --format ... refs ... amen 這樣的東西。 最后還是 UNIX。 然后它是這樣的:

  1. 有一個實用程序以其晦澀但簡潔的方式打印最后一列而廣為人知。
  2. git branch將星號放在分支名稱之前 這意味着我們似乎總是對最后一列感興趣。

結果:

git branch | awk '{print $NF}'

這是因為awk有一個區間變量NF ,它是最后一個字段的編號。 $前綴這個數字將產生該字段的內容,這正是這里需要的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM