概要

  • 共有リポジトリをリモート(CentOS)上に作成し、ローカルリポジトリ(Windows)から更新する。

インストール

  • git パッケージのインストール
    「user1」は ssh でログインできるユーザ。
    # yum install git
    # su user1
    $ git config --global user.name "Your Name"
    $ git config --global user.email you@example.com
    $ git config --global core.autocrlf false
    $ git config --global core.quotepath false
    $ git config --global core.pager "LESSCHARSET=utf-8 less"
    $ git config --global fetch.prune true
    $ git config --global remote.origin.prune true
    $ git config --global push.default simple

共有リポジトリ

共有リポジトリに作業ファイルを保持

  • 共有リポジトリ上に作業ファイルを保持し、merge も共有リポジトリ上で行う。
  • 共有リポジトリで checkout されているブランチに対して push しようとするとエラーになる。
  • 共有リポジトリの作成
    # mkdir -p /var/git/test1.git
    # cd /var/git
    # chown user1. test1.git
    # su user1
    $ cd test1.git
    $ git init --shared
    $ touch README.txt .gitignore
    $ chmod 644 README.txt .gitignore
    $ git add README.txt .gitignore
    $ git commit -a -m "first commit"

共有リポジトリは管理専用

  • 共有リポジトリは管理のみを行い、編集は作業用リポジトリ側で行う。
    # mkdir -p /var/git/test1.git
    # cd /var/git
    # chown user1. test1.git
    # su user1
    $ cd test1.git
    $ git init --bare --shared
    $ git config core.quotepath false
    $ mkdir ~/git
    $ cd ~/git
    $ git clone /var/git/test1.git
    $ cd test1
    $ touch README.txt .gitignore
    $ chmod 644 README.txt .gitignore
    $ git add README.txt .gitignore
    $ git commit -a -m "first commit"
    $ git push origin master

NAS 上に共有リポジトリを作成

  • Windows ファイル共有フォルダ内に共有リポジトリを作成する。
  • パスの区切りは「\」ではなく「/」を使用する。
    > git init --bare --shared //nas_name/path-to-repository/repository_name.git
  • 作業用リポジトリ作成(1) ブランチ 'master' をトラックする場合
    > git clone //nas_name/path-to-repository/repository_name.git
  • 作業用リポジトリ作成(2) 'master' 以外のブランチをトラックする場合(ブランチ 'develop' の例)

    > git init repository_name
    > cd repository_name
    > git remote add origin //nas_name/path-to-repository/repository_name.git
    > git fetch origin develop
    > git pull origin develop
    > git checkout develop
  • リモートリポジトリを UNC パス形式で指定すると「***.git/objects does not exist」エラーとなり push できなくなる。

Windows クライアントのインストール

  • http://windows.github.com/ からインストーラをダウンロードし実行する。
  • インストール後、GitHub アカウントの入力はキャンセルしてスキップ可能。
  • 「tools - options」を開いて初期設定を行う。
  • 「configure git」で、ユーザ名およびメールアドレスを設定する。
    コミットの際のユーザの区別に使用される。
  • エクスプローラで「%USERPROFILE%/.ssh」を開く。
    > cd /d %USERPROFILE%/.ssh
  • リモートリポジトリの ssh 用秘密鍵をここにコピーする。
    > copy repo1.example.com_rsa %USERPROFILE%/.ssh/
  • 秘密鍵ファイル名は「id_rsa」以外だとうまく動かないみたいだ。
    > copy repo1.example.com_rsa %USERPROFILE%/.ssh/id_rsa
  • 「config」を %USERPROFILE%/.ssh フォルダに作成する。
    host RemoteRepo1
      user user1
      hostname repo1.example.com
      port 22
      identityfile ~/.ssh/id_rsa.repo1.example.com
  • global 設定
    %LOCALAPPDATA%/GitHub/PortableGit_xxx/etc/gitconfig

ローカルリポジトリ設定

  • 「GitHub」は終了して「Git Shell」を起動する。
  • リモートリポジトリをローカルにコピーする。
    パスフレーズを聞かれたらタイプする。
    同名のフォルダが既にある場合はクローンに失敗する。
    > git clone ssh://user1@RemoteRepo1/var/git/test1.git
  • 作業用のブランチ(例:testing)を作成
    > git branch testing
  • master から testing へブランチを切り替える。
    > git checkout testing
  • 作業用のブランチを共有リポジトリに送信(1回目, 追跡ブランチに登録)。
    > git push --set-upstream origin testing
  • 作業用のブランチを共有リポジトリに送信(2回目以降)。
    > git push
  • 「Git Shell」から「GitHub」を起動し、SSH_AGENTの環境変数を引き継がせる。
    (今 sync しているかどうかは分かるが push に失敗する x_x;)
    > github
  • 「GitHub」に今作成した「test1.git」フォルダをドロップするとトラッキングされるようになる。
  • 以降、作業は新しいブランチ側で行い、merge は共有リポジトリで行う。
  • ファイル転送の際に改行コードの変換を行わない
    • コマンド
      > git config core.autocrlf false
    • config ファイル
      [core]
      	autocrlf = false

現在のリポジトリに別のリポジトリへの参照を追加

$ git remote add <リモートリポジトリ名> <リモートリポジトリの場所>
$ git fetch <リモートリポジトリ名>
$ git branch <リモートリポジトリ用ブランチ名>
$ git push --set-upstream <リモートリポジトリ名> <リモートリポジトリ用ブランチ名> (push 初回)
$ git push <リモートリポジトリ名> <リモートリポジトリ用ブランチ名> (push 2回目以降)
  • 「git remote add origin 」で「fatal: remote origin already exists.」が出る場合は「origin」が既に設定されている。
    「origin」を上書きするには「git remote set-url origin 」とする。

push 時に複数のリポジトリを更新

  • リモートリポジトリ 'origin' に push する際に別のリポジトリに push する。
  • pushurl が設定されていると元の url には push されなくなるので、元の url にも push したい場合は改めて pushurl に元の url を登録する必要がある。
    > git remote set-url --add --push origin <元々のurl>
    > git remote set-url --add --push origin <別のリポジトリのurl>

指定するファイルに関するコミット履歴を表示

> git log --all -- "*/filename"

リモートにあるブランチを追跡する

  • リモートのブランチを表示
    > git branch -r
      origin/HEAD -> origin/master
      origin/feature1
      origin/master   
  • origin/feature1 を追跡する
    > git checkout --track origin/feature1

ブランチ毎に別のリポジトリへプッシュ/プルする

  • リモートリポジトリ(remote1)を追加
    > git remote add remote1 <url>
  • 現在のリモートリポジトリを表示
    > git remote -v
  • ローカルにブランチ(branch1)を作成
    > git checkout -b branch1
  • 編集結果をコミット
    > git commit
  • リモート(remote1)にブランチ(branch1)を作成
    > git push -u remote1 branch1
  • ローカルとリモートの全てのブランチを表示
    > git branch -a
  • リモート(remote1)のブランチ(branch1)の更新を取り込み

    > git pull
  • How do I push a new local branch to a remote Git repository and track it too? - Stack Overflow

git 出力のリダイレクト

指定日以降のコミットログ

> git log --since="yyyy-mm-ddThh:mm:ss+09:00"

指定日間の差分

  • 差分全体
    > git diff "master@{yyyy-mm-ddThh:mm:ss+09:00}" "master@{yyyy-mm-ddThh:mm:ss+09:00}"
  • ファイル名のみ
    > git diff "master@{yyyy-mm-ddThh:mm:ss+09:00}" "master@{yyyy-mm-ddThh:mm:ss+09:00}" --name-only
  • PowerShell スクリプト GitDiffFrom.ps1
    # Show changed files between "From" and "To"
    
    Param(
      [string]$From = '',
      [string]$To = '',
      [switch]$Content = $false,
      [string]$Branch = 'master'
    )
    $repo = Split-Path -Leaf (git.exe rev-parse --show-toplevel)
    if (!$repo) {
      Write-Warning 'Not in git repository'
      Break
    }
    if (!$From) {
      $envGitDiffFrom = "GitDiffFrom_${repo}"
      $From = [Environment]::GetEnvironmentVariable($envGitDiffFrom)
      if (!$From) {
        Write-Warning "No environment variable: ${envGitDiffFrom}"
        Break
      }
    }
    if (!$To) {
      $To = Get-Date -format 'yyyy-MM-ddTHH:mm:ssz'
    }
    git.exe diff "${Branch}@{${From}}" "${Branch}@{${To}}" $(if ($Content) {''} else {'--name-only'})

タグ

  • 一覧
    > git tag
  • 絞り込み
    > git tag -l "<pattern>"
  • 追加
    shaを省略すると現在のコミットに対してタグが追加される。
    > git tag -a <tagname> -m "<comment>" [<sha>]
  • 削除
    > git tag -d <tagname>
  • 詳細表示
    > git show <tagname>
  • リモートへのプッシュ
    明示的に操作しないとタグはリモートへプッシュされない。
    > git push origin --tags
  • タグの指すコミットへのチェックアウト(一時的, ブランチを作らない)
    > git checkout refs/tags/<tagname>
  • タグの指すコミットへのチェックアウト(ブランチを作る)
    > git checkout -b <branchname> refs/tags/<tagname>

カスタマイズ

Git ユーザ切替

  • %HOMEPATH%.gitconfig を複数ユーザで切り替える。
  • %HOMEPATH%\ に .gitconfig.UserA, .gitconfig.UserB, ... のように予めユーザ分ファイルを用意しておく。
  • 下記コマンドで切り替え。
    > gitConfigSwitcher.bat UserA
  • gitConfigSwitcher.bat
    @echo off
    type "%HOMEPATH%\.gitconfig.%1" > "%HOMEPATH%\.gitconfig"

鍵ファイル作成

$ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

秘密鍵ファイル選択スクリプト

  • add-id.zip
    # 秘密鍵ファイルをリストから選択して追加するスクリプト
    
    $ssh_path = "~/.ssh/"
    $ids = @{}
    $index = 1
    foreach ( $id in Get-ChildItem $ssh_path -include id_rsa.* -Name ) {
        Write-Host "${index}: ${id}"
        $ids[$index] = $id
        ++$index
    }
    $in = Read-Host "鍵の番号を入力して下さい。"
    $key = 0
    [void][int]::TryParse( $in, [ref]$key )
    $id = $ids[ $key ]
    if ( $id ) {
        $absPath = Convert-Path "${ssh_path}${id}"
        ssh-add $absPath
    } else {
        Write-Host "入力した番号は無効です。"
    }

ssh id の追加 (CentOS)

  • ~/.bash_profile の最後に追加 (exec 以降は実行されない)
    exec ssh-agent $SHELL
  • シェルから id を追加
    $ ssh-add

リポジトリごとに秘密鍵ファイルを切替

  • GUI を立ち上げる前に Git Shell 立ち上げて「ssh-add %USERPROFILE%/.ssh/id_rsa.repo1.example.com」をやっておけば切り替えなくても大丈夫なのかな?

  • ./.git/config で秘密鍵ファイルを指定できれば、リポジトリごとに別の鍵が使えるんじゃね?

    [remote "origin"]
    	fetch = +refs/heads/*:refs/remotes/origin/*
    	url = ssh://user1@RemoteRepo1/var/git/test1.git
    	identityfile = ~/.ssh/id_rsa.repo1.example.com
  • %LOCALAPPDATA%/GitHub/PoshGit_xxx/GitUtils.ps1 に config を読み取る機能を付けてみる。
    GitUtils_2.zip
  • shell を立ち上げると確かに鍵ファイルが切り替わってるけど、GUI の方は切り替わってなくて sync に失敗する。 ← 今ココ
  • ssh-agent もプロセスが重複して立ち上がってるみたいだし、Windows 向けにはあんまり作りこんでないのかなぁ。
  • ssh-agent の終了
    > ssh-agent -k (現在のプロセスのみ)
  • ssh-agent のプロセス ID 確認
    > tasklist | Select-String ssh
  • プロセスの強制終了
    > taskkill /f /pid <プロセスID>
  • 環境変数確認
    なんで Select-String で grep できないんだろ?
    > Get-ChildItem env: | Sort-Object Name
    > Get-Item env:SSH_AGENT_PID
    > Get-Item env:SSH_AUTH_SOCK
  • GitUtils.ps1 の diff
    --- GitUtils.ps1	2012-10-22 16:43:04.253827100 +0900
    +++ GitUtils_2.ps1	2012-11-26 00:03:14.126000000 +0900
    @@ -246,8 +246,12 @@
         if (!$sshAdd) { Write-Warning 'Could not find ssh-add'; return }
     
         if ($args.Count -eq 0) {
    -        $sshPath = Resolve-Path ~/.ssh/id_rsa
    -        & $sshAdd $sshPath
    +        #$sshBakPath = Join-Path (Resolve-Path ~/.ssh/) "id_rsa.bak"
    +        #$sshIdPath = Join-Path (Resolve-Path ~/.ssh/) "id_rsa"
    +        $sshBranchPath = Resolve-Path (Get-IdentityFile (Get-GitBranch))
    +        #Copy-Item -Path $sshIdPath -Destination $sshBakPath -Force
    +        #Copy-Item -Path $sshBranchPath -Destination $sshIdPath -Force
    +        & $sshAdd $sshBranchPath
         } else {
             foreach ($value in $args) {
                 & $sshAdd $value
    @@ -285,3 +289,42 @@
         }
         git checkout -q $head
     }
    +
    +# get a identityfile for branch
    +function Get-IdentityFile($branch) {
    +    $conf_path = Join-Path (Get-Location) "/.git/config"
    +    if ( Test-Path $conf_path ){
    +        $config = @{}
    +        $block = ""
    +        foreach($line in Get-Content $conf_path){
    +            switch -regex ($line)
    +            {
    +                "^\[\s*([^\s]+)(?:\s+`"([^`"]+)`")?\s*\]$" {
    +                    if ( $matches[2] ){
    +                        $block = $matches[1] + "-" + $matches[2]
    +                    } else {
    +                        $block = $matches[1]
    +                    }
    +                    $config[$block] = @{}
    +                    break
    +                }
    +                "^\s*([^=\s]+)\s*=\s*(.*)$" {
    +                    $config[$block][$matches[1]] = $matches[2]
    +                    break
    +                }
    +                default {
    +                    #write-host "ignored: $line"
    +                    break
    +                }
    +            }
    +        }
    +
    +        $remote = $config["branch-$branch"]["remote"]
    +        $identityfile = $config["remote-$remote"]["identityfile"]
    +    }
    +    #write-host $identityfile
    +    if ( !$identityfile ){
    +        $identityfile = "~/.ssh/id_rsa"
    +    }
    +    return $identityfile
    +}

すべてのサブフォルダを更新

  • pullAll.ps1
    Get-ChildItem -Directory |
      ForEach-Object { Write-Output $_.Name; Set-Location $_; git pull; Set-Location ..; }

リンク