iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💪

Smart Git Management (gh, ghq, git-cz, lazygit)

に公開
8

I've been into watching environment setup videos by overseas engineers lately, so I'm thinking of trying Mapify.
The fact that it generates answers based on official documentation, as mentioned in "The document reading feature was convenient," is also very handy!

Introduction

I want to manage code smartly! I want to look cool! 😎

⭐️ Fun struggles ⭐️

  • Having to leave the terminal to set up a GitHub repository...?
  • Wait, where did I put this directory??
  • The penance of typing long paths to move to a project directory
  • Too many prefixes, I can't remember them all~
  • What... the commit message prefix is different for every project..?
  • Forgot the emoji & messed up the commit message 😭
  • A life of repeatedly typing cd ../ to get back to the project root...

I'm going to solve all of these!!! 💪

Getting Started Smartly

Let's start by solving these.

  • Having to leave the terminal to set up a GitHub repository...?
  • Wait, where did I put this directory??
  • The penance of typing long paths to move to a project directory

In this way, you will be able to prepare GitHub and local repositories via CLI.

demo gh ghq

Specifically, the following steps are being performed:

  1. Create a GitHub repository with the gh command
  2. Clone and git init locally using the ghq command
  3. Get a list of projects managed by ghq, select one with fzf, and move to the project directory
# Create a GitHub repository
gh repo create [project-name] --private

# Clone locally & git init
ghq get [project-name]

# Get a list of repositories managed by ghq with ^G, select with fzf and move (^G is pre-configured in .zshrc)
# If not configured, you can do the same with the following command
cd $(ghq list -p | fzf)

I will explain the installation and usage of each tool.


Operating GitHub via CLI (gh)

When you want to perform GitHub operations from the CLI, use the GitHub CLI (gh command).

The following article is helpful for installation.

When you want to create a repository:

# Private repository
gh repo create [project-name] --private
# Public repository
gh repo create [project-name] --public

If you want to delete a repository:

gh repo delete [project-name]

You can do it quickly like this.
You can also manage PRs and Issues, so please check gh --help for details.

gh --help (English version)
Work seamlessly with GitHub from the command line.

Usage
  gh <command> <subcommand> [flags]

Core commands
  auth:        Authenticate gh and git with GitHub
  browse:      Open the repository in the browser
  codespace:   Connect to and manage codespaces
  gist:        Manage gists
  issue:       Manage issues
  org:         Manage organizations
  pr:          Manage pull requests
  project:     Work with GitHub Projects
  release:     Manage releases
  repo:        Manage repositories

GitHub Actions commands
  cache:       Manage GitHub Actions caches
  run:         View details of workflow runs
  workflow:    View details of GitHub Actions workflows

Alias commands
  co:          Alias for "pr checkout"

Additional commands
  alias:       Create command shortcuts
  api:         Make an authenticated GitHub API request
  attestation: Work with artifact attestations
  completion:  Generate shell completion scripts
  config:      Manage configuration for gh
  extension:   Manage gh extensions
  gpg-key:     Manage GPG keys
  label:       Manage labels
  ruleset:     View info about repository rulesets
  search:      Search for repositories, issues, and pull requests
  secret:      Manage GitHub secrets
  ssh-key:     Manage SSH keys
  status:      Show information about relevant issues, pull requests, and notifications across repositories
  variable:    Manage GitHub Actions variables

Help topics
  actions:     Learn about working with GitHub Actions
  environment: Environment variables that can be used with gh
  exit-codes:  Exit codes used by gh
  formatting:  Options for formatting JSON output from gh
  mintty:      Information about using gh with MinTTY
  reference:   Comprehensive reference of all gh commands

Flags
  --help      Show help for command
  --version   Show gh version

Examples
  $ gh issue create
  $ gh repo clone cli/cli
  $ gh pr checkout 321

Further help
  Use "gh <command> <subcommand> --help" for more information about a command.
  Read the manual at https://cli.github.com/manual
  Learn about exit codes with "gh help exit-codes"

There is also a command to open the current repository in a browser, which is handy for sharing URLs or screen sharing.

# Open the current repository in the browser
gh browse

✌️✌️✌️✌️ Solved ✌️✌️✌️✌️
Now I'm with the terminal forever ❤️❤️

  • Having to leave the terminal to set up a GitHub repository...?

Managing Local Repositories (ghq)

What is ghq?

ghq is a tool for managing local repositories.

Since ghq manages the cloning path, you can clone without being conscious of your current directory.
It's great because it eliminates the need to move directories every time you want to clone.
It's also convenient to be able to get a list of managed repositories with the following command:

# List of repositories managed by ghq
ghq list

Installing ghq

brew install ghq

The directory path for repositories managed by ghq is specified in [ghq] root in ~/.gitconfig.

~/.gitconfig
[ghq]
 root = /Users/username/src

Cloning with ghq

You can clone repositories you manage using just the repository name.

ghq get project-name

When cloning repositories other than your own, include the owner's name.

ghq get user-name/project-name

ghq get places the cloned items in the following path:

[Directory specified in .gitconfig] + [github.com/user-name/project-name]

For example, if you clone mozumasu/dotfiles, it would look like this:

/Users/mozumasu/src/github.com/src/mozumasu/dotfiles
ghq --help (English version)
NAME:
   ghq - Manage remote repository clones

USAGE:
   ghq [global options] command [command options]

VERSION:
   1.7.1 (rev:5bf53dc)

AUTHORS:
   motemen <motemen@gmail.com>
   Songmu <y.songmu@gmail.com>

COMMANDS:
   get, clone  Clone/sync remote repositories
   list        List local repositories
   rm          Remove local repositories
   root        Show repositories' root
   create      Create a new repository
   help, h     Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version

Being able to clone to a predefined path without worrying about the current directory is low cognitive load and comfortable.
✌️✌️✌️✌️ Solved ✌️✌️✌️✌️

  • Wait, where did I put this directory??

Quickly Move to Project Directories (ghq, fzf)

What is fzf?

fzf is a fuzzy finder tool that allows you to search and select items from a list using fuzzy matching.
You'll understand once you use it. Just use it.
demo ghq fzf

Installing fzf

Install it with the following command:

brew install fzf

You might feel that ghq paths are quite long, but if you set up an alias so you can move quickly by pressing ^g, it won't be a burden anymore.

~/.zshrc
# ghq
function ghq-fzf() {
  local src=$(ghq list | fzf --preview "bat --color=always --style=header,grid --line-range :80 $(ghq root)/{}/README.*")
  if [ -n "$src" ]; then
    BUFFER="cd $(ghq root)/$src"
    zle accept-line
  fi
  zle -R -c
}
zle -N ghq-fzf
bindkey '^g' ghq-fzf

I use fzf as the fuzzy finder and bat for previews. Please replace these parts with your preferred tools.
You can install bat with the following command:

brew install bat

I am using the code provided in this article. It's a very helpful article that includes detailed explanations of the code. Thank you! 🙏

Solved! 🎉

  • The penance of typing long paths to move to a project directory

Committing Smartly

Next up is this topic.

  • Too many prefixes, I can't remember them all~
  • What... the commit message prefix is different for every project..?
  • Forgot the emoji & messed up the commit message 😭

demo lazygit git-cz
The image shows the following steps being performed:

  1. Staging files with Lazygit
  2. Executing git-cz via Lazygit to commit
  3. Pushing with Lazygit
  4. Opening the GitHub repository for the current directory in a browser using ghq browse

Making Commit Messages Beautiful (git-cz)

git-cz allows you to create commit messages that follow the standard specification called Conventional Commits.
In short, it's a tool that beautifies commit messages by enforcing prefixes and formatting.

Since you can create commit messages by selecting prefixes in an interactive format as shown below, git-cz solves the problems of not being able to remember prefixes or forgetting to add emojis.
git-cz

Installing git-cz

npm install -g git-cz

Setting Prefixes for Each Project

If there are specific prefixes for each project, you can set them by placing a changelog.config.js in the project root.

Example of a Zenn repository
changelog.config.js
module.exports = {
  disableEmoji: false,
  format: "{type}: {emoji}{subject}",
  list: [
    "add",
    "update",
    "publish",
    "unpublish",
    "delete",
    "fix",
    "chore",
    "docs",
  ],
  maxMessageLength: 64,
  minMessageLength: 3,
  questions: [
    "type",
    "scope",
    "subject",
    "body",
    "breaking",
    "issues",
    "lerna",
  ],
  types: {
    add: {
      description: "Adding a new article",
      emoji: "🚀",
      value: "add",
    },
    update: {
      description: "Updating or adding content to an article",
      emoji: "🎸",
      value: "update",
    },
    publish: {
      description: "Publishing an article",
      emoji: "🔖",
      value: "publish",
    },
    unpublish: {
      description: "Unpublishing an article",
      emoji: "🙈",
      value: "unpublish",
    },
    delete: {
      description: "Deleting an article",
      emoji: "🗑",
      value: "delete",
    },
    fix: {
      description: "Fixing typos or omissions in an article",
      emoji: "🐛",
      value: "fix",
    },
    chore: {
      description: "Changes to CI/CD or package updates, etc.",
      emoji: "🤖",
      value: "chore",
    },
    fix: {
      description: "Fixing bugs",
      emoji: "🐛",
      value: "fix",
    },
    docs: {
      description: "Updating documentation",
      emoji: "✏️",
      value: "docs",
    },
  },
  messages: {
    type: "Select the type of change that you're committing:",
    subject: "Write a short, imperative mood description of the change:\n",
    body: "Provide a longer description of the change:\n ",
    breaking: "List any breaking changes:\n",
    issues: "Issues this commit closes, e.g #123:",
  },
};
  • Too many prefixes, I can't remember them all~
  • What... the commit message prefix is different for every project..?

You can also prepare global settings.
Place global settings in ~/.chanlog.config.js.

~/.chanlog.config.js (Global settings)
~/.chanlog.config.js
module.exports = {
  disableEmoji: false,
  format: "{type}{scope}: {emoji}{subject}",
  list: [
    "feat",
    "test",
    "fix",
    "chore",
    "docs",
    "refactor",
    "style",
    "ci",
    "perf",
    "package",
    "config",
    "WIP",
  ],
  maxMessageLength: 64,
  minMessageLength: 3,
  questions: [
    "type",
    "scope",
    "subject",
    "body",
    "breaking",
    "issues",
    "lerna",
  ],
  scopes: [],
  types: {
    feat: {
      description: "New feature",
      emoji: "🎸",
      value: "feat",
    },
    chore: {
      description: "Changes related to build or libraries",
      emoji: "🤖",
      value: "chore",
    },
    ci: {
      description: "Changes related to CI",
      emoji: "🎡",
      value: "ci",
    },
    docs: {
      description: "Updating documentation",
      emoji: "✏️",
      value: "docs",
    },
    fix: {
      description: "Fixing bugs",
      emoji: "🐛",
      value: "fix",
    },
    perf: {
      description: "Performance improvements",
      emoji: "⚡️",
      value: "perf",
    },
    refactor: {
      description: "Refactoring",
      emoji: "💡",
      value: "refactor",
    },
    style: {
      description: "Changes that do not affect the meaning of the code (white-space, formatting, etc.)",
      emoji: "💄",
      value: "style",
    },
    test: {
      description: "Test code",
      emoji: "💍",
      value: "test",
    },
    // Added for personal use
    package: {
      description: "Package",
      emoji: "📦",
      value: "package",
    },
    config: {
      description: "Configuration file",
      emoji: "⚙",
      value: "config",
    },
    WIP: {
      description: "Work in progress",
      emoji: "🚧",
      value: "WIP",
    },
  },
  messages: {
    type: "Select prefix:",
    subject: "Enter commit title (summary) (optional):\n",
    body: "Enter detailed description of changes (optional):\n",
    breaking: "Enter breaking changes (optional):\n",
    issues: "Enter related issues (optional), e.g. #123:",
  },
};

Making Git Management Easy (Lazygit)

We will use the TUI tool lazygit, which makes Git management easy.
lazygit

It comes in very handy when you want to easily split or fix commits!
You can display the list of keybindings with ?. Even if you can't remember Git commands, let alone Lazygit operations, you're in safe hands.
lazygit help
It can be used in both the terminal and Neovim.
You can easily stage, commit, and push files as shown below.
lazygit

Installing Lazygit

brew install jesseduffield/lazygit/lazygit

Since typing lazygit every time is a bit of a hassle, it's convenient to set up an alias.

~/.zshrc
abbr -S lg='lazygit' >>/dev/null

Common operations in Lazygit

Stashing files before committing (Stash)

  • Stash all differences

    • s
  • Stash only staged files

    • Stage files with Space key
    • Stash the staged files with Ss
  • Reflect Stash to the stage

    • Select the Stash window and select the Stash you want to reflect with Space
    • The stashed content will be reflected

The following gif shows staging a stash and reflecting it.

Stash

Fixing forgotten changes (git commit --amend)

You can add staged files to the previous commit with A.
lazygit_amend

Editing commit messages

Move the cursor to the commit message you want to edit and press r to edit it.
lazygit_rename

You can now fix commit messages! 🙌

  • Forgot the emoji & messed up the commit message 😭

Changing the content of a commit (fixup)

If you want to modify a specific commit, follow these steps:

  • Stage the content you want to add to the commit
  • Move the cursor to the target commit and press F (a fixup commit is created)
  • Move the cursor to the target commit again and press S (auto squash)

If you are not sure what fixup is, please refer to the following article.

Undoing changes after staging (restore --staged)

You can also cancel changes after staging.

  • Move the cursor to the file you want to return to its staged state
  • du

Return to staged state

Moving commits to another branch (cherry-pick)

I often commit to the wrong branch 😭
If you have committed to the wrong branch, you can move the commit to another branch using the following steps:

  • Move to the branch you committed to by mistake with Space key
  • Move the cursor to the commit you want to move and press C
  • Move to the branch you want to bring the commit to with Space key
  • Press V to paste the commit

cherry-pick

Using git-cz from Lazygit

The Lazygit configuration file is at ~/.config/lazygit/config.yml.

I have configured it as follows so that I can execute git cz with C.

~/.config/lazygit/config.yml or ~/Library/Application Support/lazygit/config.yml
# https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md

customCommands:
  - command: git cz
    context: files
    subprocess: true
    key: C

gui:
  language: "ja"
  showIcons: true

# log customize
git:
  branchLogCmd: "git log --graph --color=always --pretty='%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' {{branchName}} --"
  allBranchesLogCmd: "git log --graph --color=always --pretty='%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --all"
  paging:
    colorArg: always
    pager: delta --dark --paging=never # delta needs to be installed to use this setting

Also, I use delta to make the diff display easier to read.

brew install git-delta

Getting Back to the Project Root Smartly

Finally, let's solve this.

  • A life of repeatedly typing cd ../ to get back to the project root...

Getting Back to the Project Root

Actually, the git command has a useful command called git rev-parse --show-toplevel.
If you set this as an alias as follows, you can move to the project root with proot.

~/.zshrc
abbr -S proot='cd $(git rev-parse --show-toplevel)' >>/dev/null

or

~/.zshrc
alias proot='cd $(git rev-parse --show-toplevel)'

Going Back to the Previous Directory

You can return to the previous directory with cd -.
If you moved from the project root, you can return to the project root with cd - as well.

Normally, it might be good to switch between cd - to return and proot if cd - doesn't get you back.

Additionally, adding the following option to your zsh configuration file will allow you to change directories without typing cd.

~/.zshrc
setopt auto_cd

However, in the case of cd -, you cannot return to the previous directory with just -.
If you want to move with just -, you need to set an alias like the following:

~/.zshrc
abbr -S -qq -='cd -'

You no longer need to repeat ../ anymore! 🎵

  • A life of repeatedly typing cd ../ to get back to the project root...

git-cz and cz-git (Updated on 12/6)


Preparing .gitignore Smartly (gibo)

gibo is a CLI tool for creating .gitignore templates.
The name seems to come from gitignore boilerplates.

You can check the list of available templates with gibo list.
You can create a .gitignore using gibo dump [template name].

I use a function like the one below:

~/.zshrc
alias gia='create_gitignore'

# Create .gitignore with gibo
create_gitignore() {
    local input_file="$1"

    # If no argument, add to .gitignore
    if [[ -z "$input_file" ]]; then
        input_file=".gitignore"
    fi

    # Select template
    local selected=$(gibo list | fzf \
        --multi \
        --preview "gibo dump {} | bat --style=numbers --color=always --paging=never")

    # Exit if nothing is selected
    if [[ -z "$selected" ]]; then
        echo "No templates selected. Exiting."
        return
    fi

    # Add selected templates to the specified file
    echo "$selected" | xargs gibo dump >> "$input_file"

    # Display the resulting file with bat
    bat "$input_file"
}

You can specify multiple templates with Tab to create a .gitignore like this:

create .gitignore

Conclusion

I hope this makes your CLI life even a little more comfortable.

Day 5 of the Advent Calendar is ucan's article "History and Evolution of JavaScript Build Tools."
As a tool enthusiast, it's a topic I can't take my eyes off.
Please look forward to tomorrow too! 🎄

Also, thank you for the comments on Zenn and Twitter! I'm very happy to be able to update my own knowledge.

GitHubで編集を提案

Discussion

unvalleyunvalley

こんにちは、自分もgit-czを使っていましたが、最近はcz-git(czg) の方を利用しています。

主な機能差分は以下にまとめられています。(実際そこまで大きな差はない気がしていますが)
https://cz-git.qbb.sh/guide/why

FYI程度でした。よければ見てみてください。

mozumasumozumasu

情報いただきありがとうございます!OSSを見たり試したりするのが好きなので教えていただけて嬉しいです!

148 MB node_modules/git-cz
1.9 MB node_modules/cz-git

👀軽量化されていてプロジェクトへの導入のハードルも下がりますね...!
日曜日くらいに使い倒してみます!

sainusainu

abbr -S lg='lazygit' >>/dev/null

これでabbrの標準出力を出さないようにしてますが、--quieterオプションをつけると同じことができるので是非。

abbr -S --quieter lg='lazygit'
or
abbr -S -qq lg='lazygit'
mozumasumozumasu

おお!!この書き方見やすくて最高にクールですね!
自分の設定ファイルが>>/dev/nullだらけで見づらいな〜と思っていたので助かります😭

本文にも追記させていただきます!もし問題等あれば教えていただけますと幸いです。🙏

Nikita KamaevNikita Kamaev

記事ありがとうございます!よくまとまっててターミナル好きの自分にとても参考になりました。

ご参考までに、MacOSにおいては設定ファイルのパスが違うようなのでもしMac前提で書かれているなら変更したほうがいいかもしれません。

Default path for the global config file:

Linux: ~/.config/lazygit/config.yml
MacOS: ~/Library/Application\ Support/lazygit/config.yml

https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md

mozumasumozumasu

コメントいただきありがとうございます!
Macのデオフォルトの設定ファイルのパスを知らなかったです😳
記事の方にも説明を追記させていただきました。
おかげさまで設定ファイルのパスの知識をアップデートできました!\(^^)/

yeharayehara

いつもためになる記事をありがとうございます!
冒頭の「ghqでgit管理しているプロジェクト一覧を取得し、fzfで選択してプロジェクトディレクトリへ移動」するためのスクリプトで紹介されてるものだと、cd が子プロセスで実行されるため、親プロセスでディレクトリの移動ができなさそうです。

ghq list | fzf | xargs -I{} cd $(ghq root)/{}

下記のスクリプトで同じことができましたので、共有です!
cd $(ghq list -p | fzf)

mozumasumozumasu

おわ~~~本当ですね!!めっちゃミスってました!!
教えていただきありがとうございます!!!