ãã®èšäºã¯ ã©ã¯ã¹ã¢ããã³ãã«ã¬ã³ããŒ2025 17æ¥ç® ã®èšäºã§ãã ã¯ããã« ããã«ã¡ã¯ïŒ ãšã³ãžãã¢ïŒå¹Žç®ã®TKDSã§ãïŒ ä»åã¯go-gitã«ã€ããŠç޹ä»ããŸãã ã¯ããã« go-gitãšã¯ åºæ¬çãªæäœ å¿çšçãªæäœ å®äŸ å·®åããã£ããã¡ã€ã«ã®ã¿SQLå®è¡ ãŸãšã go-gitãšã¯ https://github.com/go-git/go-git goã§æžãããgitæäœçšã©ã€ãã©ãªã§ãã gitæäœãšGoã³ãŒããçµã¿åãããããã®ã§ãéåžžã«æ±çšæ§ãé«ã䟿å©ã§ãã åºæ¬çãªæäœ ãŸãåºæ¬çãªæäœã玹ä»ããŸãã ä»åã¯Publicãªããžããªãªã®ã§ãèªèšŒéšåã¯çããŠãŸãã äžéšæç²ããŠèŒããŠããŸãã å
šæã¯ ãªããžã㪠ã«èŒããŠããã®ã§ãmain.goã«ã³ããããŠå®è¡ããŠãã ããã clone ãªã¢ãŒããªããžããªãcloneããæäœã¯ä»¥äžã®ãšããã§ãã r, err := git.PlainClone( "poc/" , &git.CloneOptions{ URL: "https://github.com/tkeshun/go-git-poc" , Progress: os.Stdout, // 鲿ã衚瀺ããããªããã° nil ã§OK }) ããã䜿ã£ãŠãcloneããŠããlogã衚瀺ããã³ãŒããå®è¡ãããšä»¥äžã®ããã«ãªããŸãã add addããŠã¹ããŒã¿ã¹ç¢ºèª if _, err := wt.Add(addpath); err != nil { slog.Error( "Add Error: " + err.Error()) return } st, _ := wt.Status() print (st.String()) addããŠåãæ¶ãã³ãŒããå®è¡ãããšä»¥äžã®ããã«ãªããŸãã ãªãã®æååãšãã¡ã€ã«åã衚瀺ãããŠãŸãããAãadd, ??ãã¹ããŒãžã³ã°åã衚ããŠããããã§ãã commit commitããæäœã¯ä»¥äžã®ãšããã§ãã if _, err = wt.Commit( "test" , &git.CommitOptions{ Author: &object.Signature{ Name: "go-git-invalid" , Email: "" , When: time.Now(), }, }); err != nil { slog.Error( "commit Error: " + err.Error()) } commitããã³ãŒããå®è¡ããã㊠git log ã§ã¿ããšcommitã远å ãããŠãã®ãããããŸãã ãã®commitãæ¶ããããšãã¯headã芪commitã«æ»ããŠãindexããªã»ããããã°è¯ãã§ãã â» Errorãã³ããªã³ã°æžãã®ããã©ããããªã£ãŠçç¥ããŠãŸãããå®éã¯ã ãã§ãïŒïŒïŒ headRef, _ := r.Head() headCommit, _ := r.CommitObject(headRef.Hash()) parent, _ := headCommit.Parent( 0 ) wt.Reset(&git.ResetOptions{ Commit: parent.Hash, Mode: git.MixedReset, }) ãã£ãã®commitãæ¶ããŠèŠªcommitã«æ»ã£ãŠãŸãã ãããŸã§ã§ãã䜿ãæäœã¯ç޹ä»ã§ããŸããã push(â»çªå€) äžå¿å
¬åŒã®exampleã«PushããããŸãã çŸç¶ç§ãããŸãå¿
èŠãšããŠãªãããé£ã°ããŸãã èå³ããã人㯠å
¬åŒexample ã¿ãŠè©ŠããŠã¿ãŠãã ããã ãã®ããã«ãgoã³ãŒãäžããclone, add, commit, pushãªã©ã®åºæ¬ã®æäœãå¯èœã§ãã å¿çšçãªæäœ æ¬¡ã«å¿çšçãªäœ¿ãæ¹ã§ãã æå®ããcommitããã·ã¥éã®æ¯èŒ Hashå€ãæå®ããŠæ¯èŒããããšã§å·®åã®æ¯èŒãå¯èœã§ãã ãã¡ã€ã«å·®åãåçŽã«æ¯èŒããããšãå·®åå
容ãååŸããããšãã§ããŸãã commitãé£åããã§ãªããŠãå·®åã¯åºãããšãã§ãã1commitã§è€æ°ã®ãã¡ã€ã«ãcommitããŠãããšããŠãïŒãã¡ã€ã«ãã€å·®åãåãåºããŸãã t1, _ := r.CommitObject(commit1Hash) t2, _ := r.CommitObject(commit2Hash) p, _ := t1.Patch(t2) ãã®ïŒã€ã®åŠçã§å·®åãåãåºãã p.FilePatches() ã«å·®åãå
¥ã£ãŠãã®ã§ããããã«ãŒãã§åããšãã¡ã€ã«ããšã«ïŒç¹ã®commitéã®å·®åãåãåºããŸãã for _, fp := range p.FilePatches() { } è²ã
詊ãããµã³ãã«ãèŒããããŸãã package main import ( "fmt" "log/slog" git "github.com/go-git/go-git/v6" "github.com/go-git/go-git/v6/plumbing" ) func main() { r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } commit1Hash := plumbing.NewHash( "cacf82fd8a097b8266fb7a7c84f02a9a1a599a4c" ) commit2Hash := plumbing.NewHash( "b20da7389753009b3cd6be1ae0cc66e5551e8916" ) t1, _ := r.CommitObject(commit1Hash) t2, _ := r.CommitObject(commit2Hash) p, _ := t1.Patch(t2) print (p.String()) for _, fp := range p.FilePatches() { from, to := fp.Files() if from != nil { f := from.Path() fmt.Println(f) } if to != nil { t := to.Path() fmt.Println( "file: " + t) } for _, c := range fp.Chunks() { fmt.Println(c.Content()) } } } ãã©ã³ãéã®æ¯èŒ Reference颿°ã§ãã©ã³ããšããã·ã¥ãåããã®ã§ãReference颿°ãèŠæ±ãã plumbing.ReferenceName ã plumbing.NewBranchReferenceName ãäœããŸãã Reference颿°ã䜿ããšè¿ãå€ã§ *plumbing.Reference ãååŸã§ããŸãã *plumbing.Reference.Hashã䜿ããšããã·ã¥å€ãååŸã§ããã®ã§ãããšã¯åãããã«commitããã·ã¥ã®å·®åããšãã°ããã¡ã€ã«å·®åã®ååŸãã§ããŸãã r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } ref1, err := r.Reference(plumbing.NewBranchReferenceName( "main" ), true ) if err != nil { slog.Error(err.Error()) return } fmt.Println(ref1) ref2, err := r.Reference(plumbing.NewBranchReferenceName( "test" ), true ) if err != nil { slog.Error(err.Error()) return } // ããšã¯åã ææ°çãšæ§çã®æ¯èŒ fetchãšçµã¿åãããŠäœ¿ãããšã§ãçŸåšããremoteã®ç¶æ
ãšææ°ã®remoteã®ç¶æ
ã®æ¯èŒãå¯èœã§ãã ã³ãŒãã¯ä»¥äžã®ããã«ãªã£ãŠãŸãã package main import ( "fmt" "log/slog" git "github.com/go-git/go-git/v6" "github.com/go-git/go-git/v6/config" "github.com/go-git/go-git/v6/plumbing" ) func main() { r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } remoteRefName := plumbing.NewRemoteReferenceName( "origin" , "test" ) beforeRef, err := r.Reference(remoteRefName, true ) if err != nil { slog.Error(err.Error()) return } beforeHash := beforeRef.Hash() err = r.Fetch(&git.FetchOptions{ RemoteName: "origin" , RefSpecs: []config.RefSpec{ "+refs/heads/test:refs/remotes/origin/test" , }, }) if err != nil && err != git.NoErrAlreadyUpToDate { slog.Error(err.Error()) return } afterRef, err := r.Reference(remoteRefName, true ) if err != nil { slog.Error(err.Error()) return } afterHash := afterRef.Hash() if beforeHash == afterHash { fmt.Println( "diff: no changes" ) return } beforeCommit, err := r.CommitObject(beforeHash) if err != nil { slog.Error(err.Error()) return } afterCommit, err := r.CommitObject(afterHash) if err != nil { slog.Error(err.Error()) return } patch, err := beforeCommit.Patch(afterCommit) if err != nil { slog.Error(err.Error()) return } for _, fp := range patch.FilePatches() { _, to := fp.Files() if to != nil { t := to.Path() fmt.Println( "file: " + t) } for _, c := range fp.Chunks() { fmt.Print(c.Content()) } } } å®è¡ããŠã¿ããšäžèšç»åã®ããã«ãªããŸãã ïŒåç®ã¯ããæŽæ°æžã¿ãªã®ã§diffãã§ãŸããã ãã®ããã«ãåçŽãªcommitéã®å·®åããã©ã³ãéã®å·®åãææ°åããå·®åãªã©æ§ã
ãªå·®åãååŸã§ããŸãã å®äŸ 䜿çšäŸãåºããŠã¿ãŸããã 以åãç»å£ããããšã®ããçºè¡šã§åºããããŒã«ã®ç°¡æçã§ãã å·®åããã£ããã¡ã€ã«ã®ã¿SQLå®è¡ å¿çšäŸã§ç޹ä»ããããã«ãçŸç¶ã®Headã®commit Hashãä¿æããŠãããŠfetchãããããšãŸãHeadã®commitãååŸããããšã§ãæŽæ°å·®åãååŸã§ããŸãã ããã䜿ããšããã©ã³ãã«æ°ãã远å ããããã¡ã€ã«ãæ€ç¥ããå®è¡ããããšãã§ããŸãã ã³ãŒãã¯é·ãã®ã§ãªããžããªãã¿ãŠãã ããã 差忀åºã«sqlå®è¡éšåããã£ã€ããŸãã commitãšpushããŠå®è¡ããŸãã init.sqlã¯ä»¥äžã®ããã«ãªã£ãŠãŸãã CREATE TABLE IF NOT EXISTS demo_items ( id bigserial PRIMARY KEY, name text NOT NULL UNIQUE , note text NOT NULL DEFAULT '' ); go run main.goã§å®è¡ããããšãDBã«ã€ãªãã§å®è¡ãããšããŒãã«ãäœæãããŠããããšãããããŸãã ä»åã¯åçŽãªå®è¡ã³ãŒãã§ãããããå°ãäœã蟌ããªãSQLãã¡ã€ã«ã®å
容ãã§ãã¯ãªã©ããã£ãã»ããããã§ããã insertãšselectãé çªã«ãã£ãŠã¿ãŸããã æå
¥ãšæ€çŽ¢ãããŸããããŸããã ãã差忀ç¥ããŠèªåå®è¡ãããå Žåã¯ããããå®æèµ·åããã°å·®åã®èªåå®è¡ãããŸãããããã§ãã ãŸãšã ãããŸã§ã¿ãŠããã ãããããšãããããŸããã ä»åã¯go-gitã®ç޹ä»ãšç°¡åãªå®çšäŸã玹ä»ããŸããïŒ å®éã«ç€Ÿå
çšã®ãããã¯ãã§ã䜿ãããŠããéåžžã«äŸ¿å©ãªã©ã€ãã©ãªã§ãã ãã®èšäºã§go-gitã«è峿ã£ã人ã¯ããã²gitãæŽ»ãããŠè²ã
ãã£ãŠãŠã¿ãŠãã ããïŒ