Closed14

Shell git 関連の操作 command / function を整理。

#

git command 実行する機会が多いので極力ダウンタイムを無くしたいです。
使用頻度が高いものは、極力エイリアスして覚えてます。
他にも良いスクリプトがあったら是非コメントください!

エイリアス

自分が使っている基本的なエイリアス

~/.zshrc
# git
alias g='git'
alias ga='git add'
alias gs='git status -s'
alias gd='git diff'
alias gd@='git diff @'
alias gsta='git stash'
alias gsw='git switch'
alias gsu='git submodule'
alias gc='git commit'
alias gcm='git commit -m'
alias gca='git commit --am'
alias gcoa='git rebase HEAD~ --committer-date-is-author-date'
alias gr='git reset'
alias gch='git checkout'
alias gchb='git checkout -b'
alias gp='git push'
alias gpf='git push -f'
alias gpl='git pull'
alias gf='git fetch'
alias gb='git branch'
alias gcl='git clone'
alias gcu='git config --global --list | HEAD -n 3'
alias gclr='git clone --recurse-submodules'
alias gl="git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative -10"
alias gll='git log --graph --oneline --decorate --all'
alias gst-staged="git status --short | grep '^\w.'"
alias gst-unstaged="git status  --short | grep '^\W.'"
alias gst-unstaged-tracked="git status  --short | grep '^\s.'"
alias gst-untracked="git status --short | grep '^??'"
alias gste="gst-staged | awk '{ print $2}' | xargs nvim -p"
alias grget='git remote get-url origin'

branch 切り替え

https://qiita.com/kamykn/items/aa9920f07487559c0c7e

から引用して使っている。

~/.zshrc
## local branch の切り替え
function fb() {
  local branches=$(git branch -vv) &&
  local branch=$(echo "$branches" | fzf +m --height 40%) &&
  git checkout $(echo "$branch" | awk '{print $1}' | sed "s/.* //")
}

## remote も含む branch の切り替え
function fbr() {
  branches=$(git branch --all | grep -v HEAD) &&
  branch=$(echo "$branches" |
           fzf-tmux -d $(( 2 + $(wc -l <<< "$branches") )) +m) &&
  git checkout $(echo "$branch" | sed "s/.* //" | sed "s#remotes/[^/]*/##")
}

コミット一覧 | fzf diff の表示

~/.zshrc
function fshow() {
  local out shas sha q k
  while out=$(
      git log --graph --color=always \
          --format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" |
      fzf --ansi --multi --no-sort --reverse --query="$q" \
          --print-query --expect=ctrl-d --height 60%); do
    q=$(head -1 <<< "$out")
    k=$(head -2 <<< "$out" | tail -1)
    shas=$(sed '1,2d;s/^[^a-z0-9]*//;/^$/d' <<< "$out" | awk '{print $1}')
    [ -z "$shas" ] && continue
    if [ "$k" = ctrl-d ]; then
      git diff --color=always $shas | less -R
    else
      for sha in $shas; do
        git show --color=always $sha | less -R
      done
    fi
  done
}

全ての commit object からのgit grep

Originには残ってないけど いつかしら書いた コードをキーワード検索してコミットログ id を得て、
戻って、確認するのに結構使っている。 -h でコミットログ id を非表示に。

~/.zshrc
function ggrep() {
  if [[ $2 == "-h" ]]; then
    git grep -h $1 $(git rev-list --all)
  else
    git grep $1 $(git rev-list --all)
  fi
}

git clone $user_name

GitHub API から 第一引数のユーザーのリポジトリ 100 件を取得して fzf で選択して clone
自分で作ったので結構無駄なコマンド挟んでるかも。
https://api.github.com/users/$1/repos?per_page=100 に色々パラメーター挟めば拡張できる。

~/.zshrc
function gclu() {
  curl -s "https://api.github.com/users/$1/repos?per_page=100" \
    | grep \"clone_url\" | awk '{print $2}' | sed -e 's/"//g' -e 's/,//g' \
    | fzf -m | xargs -I {} git clone {}
}

gh を使っているなら
以下のワンライナーで自分のリポジトリを検索してクローンできます。

zsh
gh repo list -L 1000 | fzf | awk '{ print $1 }' | xargs -I % git clone https://github.com/%

git status 別にスクリプトの実行

~/.zshrc
alias gst-staged="git status --short | grep '^\w.'"
alias gst-unstaged="git status  --short | grep '^\W.'"
alias gst-unstaged-tracked="git status  --short | grep '^\s.'"
alias gst-untracked="git status --short | grep '^??'"

エイリアスもう少し短くしたがいいな。

~/.zshrc
alias gs-s="git status --short | grep '^\w.'"
alias gs-us="git status  --short | grep '^\W.'"
alias gs=ut="git status  --short | grep '^\s.'"
alias gs-u="git status --short | grep '^??'"

これでかろうじて覚えられるか

アイデア募

zsh
## statged ファイルを 全てvim のタブで開く。
gst-staged | awk '{ print $2 }' | tr "\n" " " | xargs vim -p

コミット時間を指定

~/.zshrc
## $1日前
function gcb() {
  git commit --date $(date -v "$1")
}

date コマンドによるけど、確か、時間は date -v "$1H" / 月は date -v "$1M" とかだったと思う。

実行後は、Commit date とAuthor date を合わせるために
git rebase HEAD~ --committer-date-is-author-date を実行します。

VCS (Version Controll System) info を使用してプロンプトに git status を表現

~/.zshrc
autoload -Uz vcs_info
zstyle ':vcs_info:git:*' check-for-changes true
zstyle ':vcs_info:git:*' stagedstr "%F{yellow}!"
zstyle ':vcs_info:git:*' unstagedstr "%F{red}+"
zstyle ':vcs_info:*' formats "%F{green} %c%u%b%f"
zstyle ':vcs_info:*' actionformats '%b|%a'
precmd () { vcs_info; precmd() { echo } }
_vcs_precmd () { vcs_info }
add-zsh-hook precmd _vcs_precmd
PROMPT='%F{160}< %~%f${vcs_info_msg_0_} %F{160}>%f '

補足)

  • %b ブランチ情報 $hook_com[branch]
  • %i リビジョン番号またはリビジョンID $hook_com[revision]
  • %r リポジトリ名 $hook_com[base-name]
  • %R リポジトリのルートディレクトリのパス $hook_com[base]
  • %S リポジトリルートから見た今のディレクトリの相対パス $hook_com[subdir]
  • %a アクション名(mergeなど) actionformats のみで指定可 $hook_com[action]
  • %c stagedstr 文字列 $hook_com[staged]
  • %u unstagedstr 文字列 $hook_com[unstaged]
  • %m その他の情報 $hook_com[misc]

Vim からGitHub 検索

let opencmd = "!open "

function! s:gitHubSearch()
  let cw = expand("<cword>") " カーソル下の単語
  let uri = "'https://github.com/search?q=language:" . &filetype . "+" . cw . "'"
  silent execute opencmd . uri
endfunction
nnoremap <silent> <Leader>gw :call <SID>gitHubSearch()<CR>

function! s:githubSearchFile()
  let fname = expand('%:t')
  let uri = "'https://github.com/search?q=filename:" . fname ."'"
  silent execute opencmd . uri
endfunction
nnoremap <silent> <Leader>gf :call <SID>GithubSearchFile()<CR>

function! s:openGitRemote()
  let uri = system("git remote get-url origin")
  if !empty(uri)
    silent execute opencmd . uri
  end
endfunction
nnoremap <silent> <Leader>gr :call <SID>openGitRemote()<CR>

gh コマンド

zsh
gh api user --jq . | jq .

display contribute graph

zsh
curl https://github-contributions-api.deno.dev/kis9a.term

残業

面白いので導入してる。

https://qiita.com/310ma3/items/a44fee242c2076053834
~/.zshrc
function zangyo() {
  DATE=$(date -v -"$1"d "+%Y/%m/%d")
  echo "$(git config user.name)$DATE 以降の残業コミット一覧\n"
  for (( i = $1; i >= 0; i-- )); do
          DATE=$(date -v -"$i"d "+%Y/%m/%d")
          LOG=$(git log --pretty=format:"%h %ad %s" --date=format:'%H:%M' --since="$DATE 19:30:00" --until="$DATE 23:59:59" --author="$(git config user.name)" | sort -k2)
          if [[ $LOG == "" ]]; then
                  continue
          fi
          echo "[$DATE]"
          echo "$LOG\n"
  done
}

Usage:

zsh
zangyo 30

Output:

zsh
kis9a の 2021/09/28 以降の残業コミット一覧

[2021/09/29]
6ae945b 19:42 feat: update to zsh scripts
2343fbd 22:42 merged for main pc

[2021/10/14]
d3fdbb5 22:54 update

[2021/10/25]
db9b25f 23:54 neovim plugin add nicwest/vim-camelsnek

[2021/10/28]
9c354cb 22:26 update zsh script

coc-git の git.browserOpen を見てみる

便利に使ってるので簡易化してシェルスクリプト化したい。

https://github.com/neoclide/coc-git
git.browserOpen
  public async browser(action = 'open', range?: [number, number]): Promise<void> {
    let { nvim } = workspace
    let config = workspace.getConfiguration('git')

    let mode = config.get<string>('urlMode', 'normal').trim()
    let head = (await this.repo.safeRun(['rev-parse', 'HEAD'])).trim()
    let branch = config.get<string>('browserBranchName', '').trim()
    if (!branch.length) {
      branch = (await this.repo.safeRun(['symbolic-ref', '--short', '-q', 'HEAD'])).trim()
    }

    let lines: any = range && range.length == 2 ? [
      range[0],
      range[1]
    ] : [await nvim.eval('line(".")') as number]
    if (this.doc.filetype == 'markdown') {
      let line = await nvim.call('getline', ['.']) as string
      if (line.startsWith('#')) {
        let words = line.replace(/^#+\s*/, '').split(/\s+/)
        lines = words.map(s => s.toLowerCase()).join('-')
      }
    }

    // get remote list
    let output = await this.repo.safeRun(['remote'])
    if (!output.trim()) {
      window.showMessage(`No remote found`, 'warning')
      return
    }
    let names = output.trim().split(/\r?\n/)

    let browserRemoteName = config.get<string>('browserRemoteName', '').trim()
    if (browserRemoteName.length > 0) {
      if (names.includes(browserRemoteName)) {
        names = [browserRemoteName]
      } else {
        window.showMessage('Configured git.browserRemoteName missing from remote list', 'warning')
        return
      }
    }

    let urls: string[] = []
    for (let name of names) {
      let uri = await this.repo.safeRun(['remote', 'get-url', name])
      if (!uri.length) continue
      let repoURL = getRepoUrl(uri)
      let tmp = new URL(repoURL)
      let hostname = tmp.hostname
      let fix = "|"
      try {
        fix = config.get<object>("urlFix")[hostname][mode == 'permalink' ? 1 : 0]
      } catch (e) {}
      let url = getUrl(fix, repoURL, mode == 'permalink' ? head : branch, this.relpath.replace(/\\\\/g, '/'), lines)
      if (url) urls.push(url)
    }
    if (urls.length == 1) {
      if (action == 'open') {
        nvim.call('coc#util#open_url', [urls[0]], true)
      } else {
        nvim.command(`let @+ = '${urls[0]}'`, true)
        window.showMessage('Copied url to clipboard')
      }
    } else if (urls.length > 1) {
      let idx = await window.showQuickpick(urls, 'Select url:')
      if (idx >= 0) {
        if (action == 'open') {
          nvim.call('coc#util#open_url', [urls[idx]], true)
        } else {
          nvim.command(`let @+ = '${urls[idx]}'`, true)
          window.showMessage('Copied url to clipboard')
        }
      }
    }
  }

.gitignore

npx gitignore もありますが、npx が必要なので
https://gitignore.io/ から curl しています。
.gitignore は、基本 以下のソースを使うようにしています。

https://github.com/github/gitignore
~/.zshrc
function gitignore() {
	api="gitignore.io/api/"
	case "$#" in
	0)
		curl -sL "$api/$(curl -sL "$api"/list | tr "," "\n" | fzf)" >>.gitignore
		;;
	1)
		curl -sL "$api/$1" >>.gitignore
		;;
	*) ;;
	esac
}
このスクラップは3ヶ月前にクローズされました
ログインするとコメントできます