はじめに
gitコマンドの管理をGoogleSpreadSheetでした人はいても、GoogleSpreadSheet自体のgit管理はよく問題にされては今まで諦められてきました。
「GoogleSpreadSheetをgit管理するのは無理なんじゃないか」
たしかに無理です。(断定していいのか?...む、無理だよね?)
しかし、git管理に近い事はできるという事をこの記事で示したいと思います。これから紹介するコマンドは完全ではないものの、ちょっと希望を感じれるそんなコマンドです。
その名は sit
です。
対象読者
- GoogleSpreadSheetの魔改造に興味がある人
- おもちゃCLI大好きな人
- 何か新しい刺激をもとめている人
What is sit?
sit
は非常に非常に愚かなcsvコンテンツトラッカーです。gitのようにGoogleSpreadSheetCSVデータを管理したいという考えから生まれました。だから、 sit
は git
に非常に似ています。 sit
という名前はsheet git
に由来します。ですが違いがあります。git
は git
コマンドだけで完結できますが、 sit
はできません。GoogleSpreadSheetのスクリプトを管理するために clasp コマンドを使用する必要があります。
これからチュートリアルを通して説明していきます。
Tutorial
チュートリアルは以下の項目を用意しています。
- Install sit
- Initialize sit
- Create local repository
- Setting user.name and user.email
-Create remote repository
- Setting user.name and user.email
- Setting Sheet Sharing
- Edit data and Push for GoogleSpreadSheet
- Create remote master sheet
- Checkout and Edit data and Push
- Fetch from GoogleSpreadSheet
- Resolve Conflict
- Stash File Changes
- Create Pull Request
- Learn More
Install sit
まず sit
コマンドをインストールします。
npm install -g @yukihirop/sit
Initialize sit
ローカルリポジトリを作成する前に、.sitsetting
という設定ファイルを作成する必要があります。
最低限必要な設定は認証設定です。その他の設定については、SitSettingを参照してください。
$ mkdir sit-tutorial
$ cd sit-tutorial
$ sit init
created setting file: /Users/yukihirop/JavaScriptProjects/sit-tutorial/.sitsetting
.sitsetting
は以下のように生成されます。
version: 1.0.0
sheet:
gss:
auth:
credPath: ./creds.json
openAPIV3Schema:
type: object
properties:
ja:
type: string
description: 日本語
en:
type: string
description: 英語
key:
type: string
description: キー
defaultWorksheet:
rowCount: 10000
colCount: 20
repo:
local: .sit
dist:
path: ./dist
sheetName: master_data.csv
それではGoogleSpreadSheetにアクセスするための認証設定をしていきます。
GoogleCloudPlatformにアクセスし、サービスアカウントを発行します。
次に、認証ファイルをcreds.jsonとしてダウンロードします。
もし正しくダウンロードできれば sit-tutorial
のディレクトリは以下のようになってます。
これでGoogleSpreadSheetにアクセスできるようになったといいたいのですが、実はもうちょい設定が必要です。
続きは Create Remote Repository
で説明します。
📌Google Sheets APIが有効になっている事を確認してください
Create local repository
ローカルリポジトリを作成しましょう。
ローカルリポジトリの名前 .sitsetting
の設定で変更できますが、ここではデフォルトの .sit
を使用します。
$ sit repo init
created local repository: ./.sit
created dist file: dist/master_data.csv
created script files: .sit/scripts/clasp
このとき、以下の3つのファイルが生成されます。
- sit管理をするために必要なファイル (.sit以下のファイル)
- 管理対象の
dist/master_data.csv
-
GoogleSpreadSheet
に設定されるスクリプト
3番目のスクリプトファイルはリモートリポジトリを作るのに使われます。
user.nameとuser.emailの設定
gitでも設定するように user.name
と user.email
を設定すると、誰がコミットしたかなどがわかり、ログがわかりやすくなります。なので設定しましょう。
グローバル設定とローカル設定がありますが、ここではローカルに設定します。
sit config --local user.name yukihirop
sit config --local user.email yukihirop@example.com
設定されたか確認してみましょう。 .sit/config
で設定されていることがわかります。
$ cat .sit/config
[user]
name=yukihirop
email=yukihirop@example.com
Create remote repository
ここいうリモートリポジトリはGoogleSpreadSheetであり、GASスクリプトによって疑似リポジトリとして動作できるものを指します。したがって、GoogleSpreadSheetを生成し、GASスクリプトを設定する必要があります。これは clasp
コマンドを使用して行われます。
$ clasp create --type sheets
Created new Google Sheet: https://drive.google.com/open?id=1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8
Created new Google Sheets Add-on script: https://script.google.com/d/1GmpSBRbopUky8dkWTHIX88Vj47lHmH8q8melU_zkSEaM0uslRvozbj6c/edit
Warning: files in subfolder are not accounted for unless you set a '.claspignore' file.
Cloned 1 file.
└─ appsscript.json
GoogleDrive
タイプのURLのままでは使用できないため、アクセスして GoogleSpreadSheet
タイプのURLを取得します。
この例では以下のようなURLになります。
https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit#gid=0
取得したURLを origin
に設定します。これで、origin
という名前でリモートリポジトリにアクセスできます。
git
と同じですね。
$ sit remote add origin https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit\#gid\=0
正しく設定されたか確認してみましょう。
$ sit remote get-url origin
https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit#gid=0
次に、生成されたGoogleSpreadSheetにGASスクリプトを設定します。設定にはclaspコマンドを使用します。
$ clasp push
└─ .sit/scripts/clasp/Code.js
└─ .sit/scripts/clasp/RemoteRepo.js
└─ .sit/scripts/clasp/const.js
└─ .sit/scripts/clasp/util.js
└─ appsscript.json
Pushed 5 files.
$ clasp deploy
Created version 1.
- AKfycbx7zOoxj4opUQrqA0xtryXvxdYUihPgwGkeJwKmsul6wmf9V-kDbYEReZYU5Op56A7CgQ @1.
さぁ、確認してみましょう。
$ clasp open
Opening script: https://script.google.com/d/1GmpSBRbopUky8dkWTHIX88Vj47lHmH8q8melU_zkSEaM0uslRvozbj6c/edit
GASスクリプトがちゃんと設定されていることがわかります。
Setting Sheet Sharing
ダウンロードした creds.json
に書かれた client_email
のメールアドレスを使って共有設定を行います。
これで GoogleSpreadSheet
にアクセスする準備は整いました。
Edit data and Push for GoogleSpreadSheet
次にデータを更新して GoogleSpreadSheet
にpushしてみましょう。
まずはgitを使う時でもそうであるようにmasterブランチを作成してpushします。
Create remote master sheet
まずは初期状態を確認します。
$ sit status
On branch master
modified: dist/master_data.csv
no changes added to commit
$ sit branch
$ sit diff
Index: 0000000..35daa93
===================================================================
--- a/dist/master_data.csv
+++ b/dist/master_data.csv
@@ -1,0 +1,1 @@
\ No newline at end of file
+日本語,英語,キー
\ No newline at end of file
つまり、デフォルトでは、追加されるヘッダー分だけ差分が発生してます。
コミットします。(sit add
に相当するものは内部コマンドとして実行されます。)
$ sit commit -m "Initial Commit"
[master 0aa608d] Initial Commit
それではpushしましょう。
$ sit push origin master
Writed objects: 100% (1/1)
Total 1
remote:
remote: Create a pull request for master on GoogleSpreadSheet by visiting:
remote: https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit#gid=2143308413
remote:
To https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit#gid=2143308413
* [new branch] 0000000..0aa608d master -> master
正しくpushされたか確認してみましょう。
$ sit browse-remote
正しくpushされている事が確認できます。
Checkout and Edit data and Push
現在、master
ブランチにいるので、develop
ブランチにチェックアウトします。
$ sit checkout -b develop
Switched to a new branch 'develop'
$ sit branch
* develop
master
dist/master_data.csv
を以下のように修正しましょう。
日本語,英語,キー
こんにちは,hello,greeting.hello
差分を確認してコミットします。
$ sit diff
Index: 35daa93..b0122f0
===================================================================
--- a/dist/master_data.csv
+++ b/dist/master_data.csv
@@ -1,1 +1,2 @@
-日本語,英語,キー
\ No newline at end of file
+日本語,英語,キー
+こんにちは,hello,greeting.hello
\ No newline at end of file
$ sit commit -m "Initial Commit"
[develop 60243bc] Initial Commit
ログを確認してみましょう。
$ sit log
commit 60243bc893424001185de58890104bc49e853ae5 (HEAD -> develop)
Author: yukihirop <yukihirop@example.com>
Date: Sun Mar 0 18:33:57 2020 +0900 +0900
Initial Commit
コミットが確認できたので、プッシュしましょう。
$ sit push origin develop
Writed objects: 100% (1/1)
Total 1
remote:
remote: Create a pull request for develop on GoogleSpreadSheet by visiting:
remote: https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit#gid=0
remote:
To https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit#gid=0
* [new branch] 0000000..60243bc develop -> develop
シートを開いて確認してみましょう。
$ sit browse-remote
Fetch and Merge from GoogleSpreadSheet
今、 GoogleSpreadSheetの develop
ブランチが master
ブランチにマージされて以下のような状態になったとします。
📌残念ながら、GoogleSpreadSheet同士のマージは手動になります。
では、リモートブランチをfetchしてみましょう。
$ sit fetch origin master
remote: Total 1
From https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit#gid=2143308413
* branch master -> FETCH_HEAD
0aa608d..5f027c5 master -> origin/master
fetchしてきた内容を確認してみましょう。
$ sit cat-file -p 5f027c5
blob b0122f0795b0be80d51a7ff6946f00bf0300e723
parent 0aa608d96db68ac609bfb27de5036fe63a75fcd5
author yukihirop <yukihirop@example.com> 1585488810621 +0900
committer GoogleSpreadSheet <noreply@googlespreadsheet.com> 1585488810621 +0900
Merge from GoogleSpreadSheet/master
$ sit cat-file -p b0122f0795b0be80d51a7ff6946f00bf0300e723
日本語,英語,キー
こんにちは,hello,greeting.hello
fetchに確かに成功していることが確認できました。
それではfetchしてきたorigin/masterブランチをマージしてみましょう。
$ sit merge origin master
The current branch is 'develop'
Sorry... Only the same branch ('origin/develop') on the remote can be merged
📌sit
の仕様によってマージされるローカルブランチとリモートブランチは、一致する名前を持つ必要があります。
したがって、master
ブランチに一度チェックアウトする必要があります。
$ sit checkout master
Switched to branch 'master'
$ sit merge origin master
Updating b98f96a..aab61a3
Fast-forward
dist/master_data.csv
1 file changed
dist/master_data.csv
を確認すると、マージされていることが確認できます。
$ cat dist/master_data.csv
日本語,英語,キー
こんにちは,hello,greeting.hello%
Resolve Conflict
今、GoogleSpreadSheetのmaster
を手動で変更したとします。
この状態で fetch
して merge
します。
$ sit fetch origin master
remote: Total 1
From https://docs.google.com/spreadsheets/d/1QPNoQcKGQrk1U9_5RP42y7YbWZMcPcN-Vn96wO2ZCh8/edit#gid=1549813878
* branch master -> FETCH_HEAD
87fbc98..00323a3 master -> origin/master
$ sit merge origin master
Two-way-merging dist/master_data.csv
CONFLICT (content): Merge conflict in dist/master_data.csv
two-way-merge failed; fix conflicts and then commit the result.
コンフリクトが置きました。 sitオブジェクト
をリモートリポジトリ(GoogleSpreadSheet)で管理できないため
three way mergeが使えず、two way mergeでマージを行っているのでコンフリクトは起きやすくなってます。
この状態でのマージの様子を見てみましょう。
$ sit merge --stat
fatal: You have not concluded your merge (MERGE_HEAD exists)
Please, commit your changes before you merge.
「MERGE_HEAD
が存在している。競合を解決してください」というメッセージがでます。
ここで一旦、mergeを取り消してみましょう。
$ sit merge --abort
$ sit merge --stat
Already up to date.
元にもどりました。gitと同じで --abort
で取り消す事が可能です。
もう一度、merge
してみましょう。
$ sit merge origin master
Two-way-merging dist/master_data.csv
CONFLICT (content): Merge conflict in dist/master_data.csv
two-way-merge failed; fix conflicts and then commit the result.
次のようにコンフリクトを解消して、 --continue
をしてみてください。
$ sit merge --continue
hint: Waiting for your editor to close the file...
起動したファイルを閉じ、Ctrl + Cを押してマージを完了します。
$ sit merge --continue
hint: Waiting for your editor to close the file...
^C
それではmergeの状態を調べてみましょう。
$ sit merge --stat
Already up to date.
$ sit log
commit aab61a36fff2bf2dacc87d416a83b6c0e15a569c (HEAD -> master)
Author: yukihirop <yukihirop@example.com>
Date: Tue Mar 2 00:21:45 2020 +0900 +0900
Merge from GoogleSpreadSheet/master
commit b98f96a746dba25e2c927beda41cf9fa361a44ab
Author: yukihirop <yukihirop@example.com>
Date: Tue Mar 2 00:11:31 2020 +0900 +0900
Initial Commit
マージが成功しました。
Stash File Changes
次のようにファイルを編集して、stash
してみましょう。
$ sit stash save
Saved working directory and index state WIP on master: 5fe2e48 Merge remote-tracking branch 'origin/master'
stash
した結果の詳細を確認してみましょう。 dist/master_data.csv
ファイルを確認してみましょう。
$ sit stash list
ffe9070 stash@{0}: On master: WIP
$ sit stash show -p stash@{0}
Index: 9bb9157..0e90f47
===================================================================
--- a/dist/master_data.csv
+++ b/dist/master_data.csv
@@ -1,3 +1,4 @@
日本語,英語,キー
こんにちは,hello,greeting.hello
-おやすみ,good night,greeting.good_night
\ No newline at end of file
+おやすみ,good night,greeting.good_night
+歓迎します,wellcome,greeting.wellcome
\ No newline at end of file
stash
した内容の復元をしてみましょう。 stash apply
を使います。
📌stash@{0}
は省略できます。stash@{n} (n>0)
は省略できないです。
$ sit stash apply stash@{0}
On branch master
Changes not staged for commit:
modified: dist/master_data.csv
no changes added to commit
もう一度、stash
して、 次はpop
でもどしてみましょう。
$ sit stash save "WIP"
Saved working directory and index state On master: WIP
$ sit stash list
2f077ef stash@{0}: On master: WIP
7284116 stash@{1}: WIP on master: 08d2106 Merge remote-tracking branch 'origin/master'
$ sit stash pop
On branch master
Changes not staged for commit:
modified: dist/master_data.csv
Dropped stash@{0} (2f077ef6551e339e1808e6fbb38f27424be46b8d)
gitと同様に、popはスタッシュの内容を削除します。
$ sit stash list
7284116 stash@{0}: WIP on master: 08d2106 Merge remote-tracking branch 'origin/master'
Create Pull Request
GoogleSpreadSheet
に、master
とは異なる名前(たとえば、develop
)という新しいシートを追加しましたが、GitHubのようにdevelop
ブランチからmaster
ブランチにプルリクエストを作成することもできます。
master
develop
develop
ブランチからmaster
ブランチにプルリクエストを作成してみましょう。
$ sit pull-request origin master...develop
Total 1
remote:
remote: Create a pull request for 'master' from 'develop' on GoogleSpreadSheet by visiting:
remote: https://docs.google.com/spreadsheets/d/1xZ6egbhsuqIh8kMsfeg8vCkyFGabZQybcxKuc2F7Le4/edit#gid=2115668596
remote:
To https://docs.google.com/spreadsheets/d/1xZ6egbhsuqIh8kMsfeg8vCkyFGabZQybcxKuc2F7Le4/edit#gid=2115668596
Please look at sheet: '[pr] master...develop' in GoogleSpreadSheet
📌実行されるたびにローカルリポジトリに変更を生成するpush
コマンドとは異なり、pull-request
コマンドはステートレスコマンドであり、何度でも実行できます。
GoogleSpreadSheet
をご覧ください。[pr]master...develop
という名前でシートが作られていると思います。
📌[pr]
で始まるシート(ブランチ)名は、プルリクエスト用に予約されているため、ブランチ名として使用できません。
他のシートと違いフッターがあると思います。それについて説明します。
name | description |
---|---|
created at |
作成日時 |
reviewers |
レビュワー |
assignees |
PRを作成した人 |
message |
PRの内容を簡潔に書く |
labels |
自由に活用してください |
projects |
自由に活用してください |
milestone |
自由に活用してください |
少なくとも reviewrs
・ message
はかくべきだと考えます。
次に、[ステータス]列について説明します。 [ステータス]列では3つの記号が使用されています。
symbol | description |
---|---|
+ | 追加 |
- | 削除 |
± | 追加される側にだけ存在 |
つまり、現在の例では、index==2
の行が変更され、index==3
の列はmaster
シートにのみ存在し、そのままです。master
シートへのマージはサポートされていないため、問題がなければ、責任者が手動でmaster
シートを更新してください。
Learn More
その他のコマンドの使い方などをまとめた公式ドキュメントがあります。そちらをご参照ください。
おわりに
このコマンドを作ったきっかけは会社で引き継いだプロジェクトの翻訳ファイルがGoogleSpreadSheetで管理されていて、それを目で読み取ってyamlファイル(ja.yamlとen.yaml)として別途作成して管理していいる現状を目にして「なんや...この管理のやり方は...」と思ったのが全てのきっかけだった。GoogleSpreadSheetで管理されているのはデザイナーとのやり取りの都合上、仕方のないものだと言われた。手動で作った翻訳ファイル(ja.yaml・en.yaml)がGoogleSpreadSheetで管理されているものと一致していれば、「 まぁ...こういうやり方もあるのかな? 」 で終わっていたのかもしれないが、かなりズレていて合わせるための修正のだるさから「 gitみたいに管理できたらなぁ 」と強く思ってしまった。
それを形にするまでには、大変だった。 まず Git の仕組み (1) のブログと p10.1 Git Internalsを読んで「gitオブジェクトとは何か?」を勉強して、 Write yourself a Git!というpythonでgitコマンドを割と再現していく練習をこなしてイメージを掴んだ後、実装に取り掛かった。gitのソースコードを見たがちっともわからなかったので、 git init
したプロジェクトにできた .git
の中で git init
して各コマンドを叩いた後のファイルの変化をおって想像で実装した。gitのソースコードを見てわかったのは全てC言語で書かれているのかなって思っていたけど、perlで書かれていたり、shellscriptで書かれていたりとバラバラだった事とstashコマンドを作ったのは日本人女性だったという事である。
調査なども含めて3ヶ月〜4ヶ月くらいはかかったがとりあえず最後までやり遂げる事ができてよかった。
せっかく作ったのでGoogleSpreadSheetで管理されている翻訳ファイルをローカルにcloneしてローカルに持ってきて、今まで手動で作っていた翻訳ファイル(ja.yaml・en.yaml)を自動で作成するようにやってみようと思う。