修改git提交的历史

前言

如果是团队协合中的git仓库,其历史最好不要修改;但如果是没有别人参与的个人项目,那就随便了,自己开心就好。毕竟,历史的条理与历史的真象,在哲学上就是一对矛盾。以下所有操作前,都最好把整目录备份一下,方便出错时从头再来。

修改最近一次提交及指定提交时间

git commit --amend --date "Wed, 01 Jan 2020 15:40:30 +0800"

commit 命令的 --amend 参数这个很简单,应该都用过。主要是 --date 这个参数,用来手工指定本次提交的时间(而不是默认的上次提交时间当前前系统时间),但是,它的日期格式 GIT_COMMITTER_DATE 不是汉语日常习惯的日期格式,很不方便手写,可以通过date命令做转换,如下(注意其中的三层引号是不同的,如果不熟悉bash可以死记,从外到内分别是单、反、双):

git commit --date '`date -R -d "2020/1/1 15:40:30"`'

删除文件

假设要删除passwords.txt的文件,让在整个git历史像不存在过一样

git filter-branch --tree-filter 'rm -f passwords.txt' HEAD

上面命令默认保留空提交,即删除文件后某些提交会成空提交,如清空这些空提交,可加入参数 --prune-empty

单个文件改名/单目录改名

git filter-branch --tree-filter 'if [ -f old-name.txt ]; then mv old-name.txt new-name.txt; fi' HEAD
git filter-branch --tree-filter 'if [ -d old-name ]; then mv old-name new-name; fi' HEAD

如上两行,分别是对单文件改名(old-name.txt  -> new-name.txt), 单目录改名(old-name -> new-name) ,事实上并没区别。

注意 .git/refs/original/refs/heads 目录要为空。否则会报错说"Cannot create a new backup. A previous backup already exists in refs/original/  Force overwriting the backup with -f",然而加-f参数似乎并没有用;可直接删除目录 .git/refs/original。  执行过该命令,就会生成一次.git/refs/original/refs/heads/master,大概是filter-branch的后悔药(备份目录)

如果有多个分支,可以强行将分支们合并,改过名后再滚回到合并前,这样通常更方便些:可一次完成所有分支里的改名,同时避免被一大堆各种分支搞晕。(方法来源于stackoverflow,具体链接忘了)。详细参考:Pro Git-重写历史

子目录变根目录

要把某个子目录foodir/ 下的所有文件(包含其历史),独立出去,成为一个单独的项目。其它文件自然被丢弃了,所以操作前要把整个仓库备份一下,或者在克隆的新仓库上操作。

git filter-branch --subdirectory-filter foodir -- --all

整个项目作为项目的子目录(根目录改子目录)

因为功能扩充、重构等原因,要把项目所有文件移到子目录里,git mv 不能被真正的跟踪。按如下操作,可移到newsubdir子目录中

git filter-branch --index-filter \
'git ls-files -s | sed "s-\t\"*-&newsubdir/-" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD

参看git联机文档 git filter-branch --help

合并提交(把多次commit合并成一个)

变基(rebase)相关操作,如 git rebase -i HEAD~3 更多参考git-book 重写历史

修改提交历史中的邮箱地址

git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
then
GIT_AUTHOR_NAME="Scott Chacon";
GIT_AUTHOR_EMAIL="schacon@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD

删除指定姓名的所有提交历史

这样作法不好,跟这人得多大的仇

git filter-branch --commit-filter '
       if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ];
       then
               skip_commit "$@";
       else
               git commit-tree "$@";
       fi' HEAD

从所有git历史版本中修改某个文件中指定内容(字串替换)

某个文件已经有多个历史版本,但里面有某些宜的内容(比如用户名密码),因此希望保留某完整的历史,而不是简单的将其所有历史中删除,而只把指定字符串替换掉。

git filter-branch --tree-filter '
  if [ -f create_table_sql.txt ];
  then
    sed -i "s/realpasswd/fackstring/g" create_table_sql.txt;
  fi' HEAD

自由的编辑(交互式,而非前面的批处理式)

  1. git rebase -i <commit> 命令打开交互式 rebase 编辑器(若要从第一次提交开始修改,可以使用 git rebase -i --root 命令)。
  2. 在编辑器中,将需要修改的提交行的前缀由 pick 改为 edit,然后保存并关闭编辑器。
  3. Git 将会逐个应用每个提交,并在需要修改的提交上停止。在停止处,可以自由的编辑,然后使用 git commit --amend 命令修改提交,或使用 git reset HEAD~ 命令取消提交并将文件恢复到未提交的状态。
  4. 如果把新近的 commit 拉到早期历史中,其日期将维持,这样插在历史记录显得很突兀;可以使用 --date 参数任意指定。不过日期格式 GIT_COMMITTER_DATE 很不方便手写,可以在shell里通过 date -R -d "2020-05-15 20:30:40" 转换,此二者是一致的,参看前面第一节中所述。
  5. 完成修改后,使用 git rebase --continue 命令继续 rebase 操作,直到所有的提交都被修改或应用。
  6. 如果在 rebase 过程中出现任何问题,可以使用 git rebase --abort 命令取消 rebase 操作并回到修改前的状态。

.EOF.

一系列git设置选项/fetch,push走socks5代理/一份简单的gitignore文件

简述

git的设置一般通过 git config 命令,在本地仓库里的实际配置文件为 .git/config ,全局设置则在操作系统用户目录下 ~/.gitconfig

网络代理

连接远端服务器的操作,如fetch, push 等,如果需要走代理的情况,如果 远端仓库是 http 协议,则比较简单,  git config http.proxy=http://127.0.0.1:1080

不过,通常情况下 ssh 协议使用更广泛,这就要借助ssh的设置了(而不是git本身的设置),配置文件是 ~/.ssh/config  ,比如设置 github.com走1080端口的socks5代理,示例如下,注意其中 ProxyCommand  一行

Host github.com
HostName github.com
Port 22
# #User feng
IdentityFile ~/.ssh/id_rsa
ProxyCommand /usr/bin/nc -X 5 -x 127.0.0.1:1080 %h %p

基于ssh的用户认证

鉴于ssh协议使用更方便,这也就需要其配置文件,~/.ssh/config  , 如上节的示例,指定了对应的用户(User)的ssh密钥文件(IdentityFile) ,上面的其实就是ssh默认选项,一般情况下,不需要另外配置,这样写,只是方便参照做修改自定义。当然还有更多选项,可参考ssh的手册。

一份简单的gitignore文件

如下

*.bak
*.swp
.idea/
*.bak
*.~
.DS_Store
nbproject/*
*.swp
*.iml
_git_*
*_bak*
logs/
*.log
data/
uploads/
_build

没了,有需要的时候再往里面加。

主要是个人使用,放这里备忘。

在cygwin里调用windows版git-scm的gitk/git-scm与cygwin协同工作

20161216更新:

如果安装了git-scm的  Git-2.11.0-64-bit.exe,将如下命令加入~/.bashrc,重启cygwin即可(32位版本及以后的新版本应该是类似的)。

alias gitk='/cygdrive/d/Program\ Files/Git/cmd/gitk.exe'
. /cygdrive/d/Program\ Files/Git/mingw64/share/git/completion/git-completion.bash

假设git-scm安装在 D:\Program Files\Git

上面第一行命令是把gitk别名到windows版gitk的程序位置。第二行是执行git-completion以让git支持自动补全,方便使用,注意开头点号后面有个空格。


git-scm windows版的安装选项

以从 git-scm官方下载的Git-2.11.0-64-bit安装为例,典型的windows程序安装流程,可以一路下一步到底;不过有几步强烈建议按下面的选择

安装位置 ,推荐安装到D盘;重装windows后git还可直接使用

windows shelll集成选项。如果平时主要使用cygwin里的命令行版git、windows版只使用gitk看版本历史(推荐),建议全部取消选择。当然这里也可以保持默认选项。(新版本的git-scm在这里增加了几个选项,其中一项为 "Git LFS(Larget File Support)",推荐选中;其它的,自然就没必要。 [update 2019/09/15] 

强烈建议选第一项,只在Git Bash里使用git。强烈反对在windows cmd中使用git。推荐平时使用cygwin里的命令行版git。

强烈建议选第三项,不要让git自作聪明的修改换行风格,以避免被同伴鄙视+痛恨!

保持默认。强烈反对在windows cmd中使用git bash。事实上更推荐使用cygwin里的shell bash


旧文章归档

下面是操作方法适合早期版本,具体截止哪个版本号不明;内容是2014年底所写。

[标题] 在cygwin里调用windows版git-scm的gitk/git-scm与cygwin协同工作

-----------本文前面部分废话比较多,可以直接跳过前五段,阅读下一条分隔线后--------------------

前言

cygwin作为潜伏在windows里的类unix/linux操作系统,堪称神器,几乎可以运行一切unix工具,甚至连gui版的unix软件也可以用cygwin-X模拟。但是这个X实在太丑陋了,使用也不方便。不过,事实上,我们用cygwin主要是使用grep, cut, vim, git 等这些经典的的unix工具,而不是gui;毕竟gui也不是unix/linux的擅长项。

对于使用git的朋友,应该更依赖cygwin下的命令行版git,自由,快捷,随心所欲。然而要查阅版本历史时,还是gui版的git更直观。

而windows版git-scm自带了git gui/gitk工具,可以在git bash里运行gitk调用,这点与unix/linux下的git一致,使用还是比较方便的;但git bash有个硬伤,工具太少,而且shell太傻,完全是windows命令提示符的风格,通过鼠标做选择复制等操作,几乎是脑残得令人无语;比cygwin差了N个星系的距离。

于是:如果能在cygwin直接调用win32下git-scm的gitk,就完美了。

然而,一直没有找到方法,连万能的google也没帮上忙。经过艰苦卓绝的不懈努力,终于找到了办法,并发现多次尝试错误的原因。

----------------- 前面废话多;下面是原理,不想看可跳到下条分隔线(十段之后) ------------

原理简析

假设win32版git-scm装在 D:\Program Files\Git ,那么gitk位于 D:\Program Files\Git\bin\gitk 该文件是一个bash风格的脚本,它是由D:\Program Files\Git\bin\wish.exe 所调用执行的。

在windows开始菜单的git子项里,查看“git gui”的属性,会发现它是一个快捷方式,指向 "D:\Program Files\Git\bin\wish.exe" "d:\Program Files\Git\libexec\git-core\git-gui"  其中的gui-gui同样也是个bash风格的脚本,由wish调用。

因为cygwin本身支持windows原生的win32程序,执行该wish.exe并带上相应的参数,就可以调用这些bash风格脚本。

原理就是如上这些。我们按照cygwin风格重写这些命令。

在cygwin里,windows盘符挂载到/cygwin/下,形式如/cygdrive/c, /cygdrive/d ... 那么,上述wish.exe 在cygwin里,路径即

/cygdrive/d/Program\ Files/Git/bin/wish.exe
路径中的空格,要做转义。

后面要带上参数,这个参数要使用windows风格的路径,因为wish.exe是win32程序,它的参数是win32风格的。gtk的脚本即如下

/cygdrive/d/Program\ Files/Git/bin/wish.exe "D:\Program Files\Git\bin\gitk"

wish的参数加了双引号,这也是win32风格。可以在cygwin下切换到一个git项目目录里,执行上面的命令测试一下。

以cygwin里创建一个命令别名 gitk 到上面的命令上,如下

$ alias gitk='/cygdrive/d/Program\ Files/Git/bin/wish.exe "D:\Program Files\Git\bin\gitk"'

然后执行gitk即可,与git-scm的bash环境里执行gitk一样。不过这只能在本次会话中有效,将其放到 ~/.bashrc 里,后面增加如下一行

alias gitk='/cygdrive/d/Program\ Files/Git/bin/wish.exe "D:\Program Files\Git\bin\gitk"'

大功告成。

如果需要在cygwin里使用git gui的话,方法类似。

-----------------只想看解决方案者,可以直接从这里开始看 --------------------

一句话操作

假设win32版git-scm装在 D:\Program Files\Git  .  在cygwin里修改 ~/.bashrc 文件,追加如下一行:

$ alias gitk='/cygdrive/d/Program\ Files/Git/bin/wish.exe "D:\Program Files\Git\bin\gitk"'

保存后,启动cygwin即可。

如果不明白,建议看前文的原理部分

git生存手册/git入门基本教程

本文针对git初学者入门之用,目标为“快速的在git世界里生存下来”。当然,生存下来是不够的,严重推荐git-scm上教程深入学习,另外git客户端、开发库、部署方案等参看git wiki

本文主要以命令行操作为主。

git安装,不多说。

linux或mac os x下,应该已经安装过了,如没有请通过yum/apt-get等安装。windows下可以使用git-scm的win32版本,自带命令行;推荐在cygwin下的git。可参考 git-scm下载合适的版本

第0步,基础知识。

git是分布式、去中心化的。通俗的说,git项目里的就是每个人都有所有版本的副本;不要服务器也能工作。

几乎所有的git的操作,都被集成到git 这个命令下,如 git add, git branch, git commit, git push 等,可以运行 git --help 浏览一下。

第一步,配置个性化的名字

运行下面的命令,配置一下你的名字与邮箱地址。注意花括号只是为了看起来方便,实际执行不要带它们,除非你的名字带花括号。但强烈不建议使用特殊符号,以免为自己挖抗!

git config --global user.name {你的名字}
git config --global user.email {你的邮箱地址}

第二步,忘掉“服务器”的概念,并搞个git库。

先不考虑服务器;尤其如果有svn使用经历的话,先忘掉这个概念。因为git是“去中心化”的,即不要服务器就可以工作。所以一开始我们先简化掉不必要的东西,有助于快速入门。

打开命令行。windows下使用git-scim者,可以在某个文件夹里点右键-Git Bash (?)。切换到一个空目录里。下面以/cygdrive/e/git_repo/foobar/ 目录为例(cygwin的目录风格)

执行命令 git init ,你将看到类似下面的提示消息

Initialized empty Git repository in /cygdrive/e/git_repo/foobar/.git/

看到了吧,git在本目录下创建了一个子目录 .git 这就是本地的git仓库。受git控制的文件的所有历史版本都会存储在这里面;本git仓库的配置也在里面。有兴趣可以到里面随便看看。有探索精神的童鞋注意了,不要指望在里面按文件找到你的文件;请在入门后了解一下git原理。

第三步,熟悉一下git环境。

回到前面创建的目录(注意不要跑到.git子目录里),运行 git status,你将看到类似如下的消息:

$ git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

简单说明一下,On branch master  -> 你在 master 分支(branch)下

Initial commit  -> 当前是初始提交。刚刚初始化的一个版本库,即是空的 (别想歪,没字)。

nothing to commit (create/copy files and use "git add" to track)  ->  接上行“初始提交” 但没有任何东西待提交(创建或拷贝进来一些文件并使用git add 追踪它们)。

[提示] git status这个命令非常常用。

第四步,搞几个文件,纳入git版本库。

按照git的提示,随便搞几个文本文件,最好小一点,只有几行。从别的地方拷来也行,在这里新建也可以。

假设创建了文件  readme.txt ,内容如下3行,最后一行是空行

this is a text file
line 2 aaa
line 3 bbbbb

再运行一下git status

On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        readme.txt

nothing added to commit but untracked files present (use "git add" to track)

消息变了,大意是说:

有了未追踪的文件(Untracked files):
(使用 git add <文件名> ... 把你需要追踪的文件加入进来。)
readme.txt

现在还没有添加到待提交列表里,但是有未追踪的文件,使用git add 追踪它们.

有点啰嗦.....

git 里,文件被追踪(track),就是被纳入git版本控制。每次提交(commit)就是每一次的版本变更。

那我们就按提示添加文件到追踪列表吧.

git add readme.txt

咦,没有任何消息?是的,再运行git status看看。多了如下的部分,不细讲了

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   readme.txt

第五步,首次提交一个版本

运行命令 git commit -m "first commit, file readme.txt"

本行命令有点小麻烦,因为带了个 -m 参数,后面双引号里是一段简短的说明,为本次提交加点注释,方便以后查阅。git默认强制要求每次提交都要带说明。注释随便写,但强烈建议有概括性,简洁直观的文字说明,会给你及你的团队带来帮助。

[本步下面的内容非必须,但还是建议了解一下] 如果不带 -m 参数,git默认会给你一个vim 的编辑器,并给出一些有意义的提示文字,你可以在这里写注释,然后保存退出,git会把你写的内容当作注释。

vim编辑器是神器,但入门有门槛。对于 git 提交来说,只要死记下面的步骤即可:

默认是不是打字的,要先敲一下 i 键(注意是小写状态),即可以正常输入文字。

写完确认无误后,按 Esc 键,按一次或N多次都行,如果不确定是否真的按下过,那就多几次,反正没有副作用。然后击键:

:wq

然后按回车。提交就完成了,将大概如下所示的消息。

$ git commit
[master (root-commit) fbc00df] first commit, file readme.txt
 1 file changed, 3 insertions(+)
 create mode 100644 readme.txt

啰嗦 一下,前面保存注释用的是三个键,冒号、w、q,然后回车。

第六步,做下修改,再提交一个版本

随便修改一下前述文件,然后运行 git status ,消息大概如下 :

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

说得很明白,readme.txt这个文件有修改,你可以通过 git add 添加到待提交列表。这个“待提交列表”有个名字“暂存区域”。

消息里还说,可以通过git checkout -- <file> 丢弃你做的修改。这点下一步再尝试。

执行git add readme.txt ,然后执行 git commit -m "add a new test line",完成本次提交。

第七步,体验丢弃修改功能

随便在readme.txt里修改点东西,然后git status,看到git认出来 了文件的修改。消息从略

然后执行  git checkout -- readme.txt

注意命令里的两个连字符(--) ,其前后都是有空格的。

然后再执行git status,消息如下:

$ git status
On branch master
nothing to commit, working directory clean

查看readme.txt文件,会发现你做的修改确实已经没了,恢复为上次提交时的内容。

当然,这个命令只能恢复到上次提交时的版本,要想回复到更早的版本,继续下面

第八步,历史长廊

先随便做几次提交吧,可以多建几个文件并加到追踪里。

差不多了,就通过git log 查阅提交历史,大概如下

$ git log
commit 9ab41ac68ae6fe8e0e3ccc0013af91e5d3aa0ca9
Author: fengyqf <fengyqf@gmail.com>
Date:   Thu Nov 20 21:15:55 2014 +0800

    remove a line

commit f72675d78dfa6c5a7c0a9e742fb3e5a19d9d62d7
Author: fengyqf <fengyqf@gmail.com>
Date:   Thu Nov 20 21:15:28 2014 +0800

    add serival lines

commit e063544aacdff53f4d971179720bd1f83f1f9634
Author: fengyqf <fengyqf@gmail.com>
Date:   Thu Nov 20 21:13:48 2014 +0800

    append a line

commit fbc00dfcc90e395aa0491a4aaaca7659d74b9d5d
Author: fengyqf <fengyqf@gmail.com>
Date:   Thu Nov 20 20:41:47 2014 +0800

    add some file

这里按时间倒序列出最近的提交。如果你提交次数较多,你会看到屏幕最后一行行首是一个冒号,紧跟着光标。这时,你可以按空格键查看下一屏,或 b 键返回上一屏,或Esc键退出回到命令行(shell)下。

看上面的日志,每次提交一节,每节首行是 commit 后面跟着本次提交的ID,这段id是即是版本号,当然它对人类不太友好。第二三行是提交人、时间,然后空一行接着是本次提交注释文字。

如果我们的某个文件被玩坏了,需要恢复到历史上的某个版本,那就git checkout 出来

假设需要恢复上上次提交

commit f72675d78dfa6c5a7c0a9e742fb3e5a19d9d62d7

执行 git checkout f726 readme.txt

其中 f726 是该次提交的ID前4个字符,也就是,通常我们不需要写完整的ID就可以指定某次提交。当然,你使用前5个字符或前6个或更多,都是可以的。

提交ID后台跟需要检出的文件名。注意一定要带这个“文件名”的参数,不然会出乱子:下次提交后,在该版本后的版本将会被你抹掉!

看到了吧,文件readme.txt 历史版本又回来了。

差点忘了,可以通过git checkout --help查阅帮助,其它命令,也可以同样查帮助。帮助内容自己看,这里不讲了。

第九步,分支

git最强悍的功能之一,就是它的分支,比svn彪悍得多!

是否注意到前面所说的“master”分支,这是默认分支,我们可以创建一个新的分支。

git branch -b

第十步,比较

git diff

第十一步,分支合并

git merge

第十二步,checkout --patch

git checkout --patch

第十三步,远程仓库

git remote add

git push

git fetch

git pull

一系列git技巧:bash下让git支持命令自动完成etc

bash下让git支持命令自动完成

下载git-completion 的脚本 https://github.com/git/git/blob/master/contrib/completion/git-completion.bash 放到一个符合你习惯的位置,我个人放在 ~/script/git/ 里。(事实上该文件是 git源码中的文件 contrib/completion/git-completion.bash

个人习惯在把一些供个人使用的脚本放在家目录下的script目录,这里为git建一个单独的目录,将上述文件放进去。

然后修改~/.bashrc文件,加入一行

. ~/script/git/git-completion.bash

重新登录,在bash下,你的git就支持自动补完了,输入 git com,然后按两次tab键,即见效。

不重新登录,也可以立即生效。当前的bash里运行上述命令. ~/script/git/git-completion.bash即可。

其他小技巧

git diff 忽略dos与unix换行符的差异:git diff  --ignore-space-at-eol

linux/centos上安装配置gitosis(git服务器端)

gitosis长期没有更新了,换gitolite吧,或gitlab也行

[个人按本文方法是可以安装gitosis的,但在客户端git clone总是失败,也无法成功创建repo,所以个人改用gitolite,感觉比gitosis简单一些,而且一安装就可以正常工作]

安装gitosis,因为centos/redhat官方源不带gitosis,所以需要先添加EPEL软件仓库,或者手工下载gitosis的 rpm包及依赖包并手工安装。

gitosis rpm包安装后,会自动创建一个gitosis用户,为了简化其见,手工对其改名,改成git,涉及以下四个文件/etc/passwd, /etc/shadow, /etc/group, /etc/gshadow . 默认情况下,git用户(即改名前的gitosis用户)的home目录为 /var/lib/gitosis

这是git用户是作为git的管理账号之用。因为此时git用户没有设置登录密码,我们也不需要其密码,而是使用ssh密钥登录。但对gitosis的设置还是使用git用户登录比较方便。可以切换到root用户,然后 su git .

为git用户设置ssh密钥登录。上传一个ssh公钥,建议使用你的常用账号下的ssh公钥。并将其加入git用户的authorized_keys

sh-4.1$ cd ~/.ssh/
sh-4.1$ cat id_rsa.pub >>authorized_keys

确认可以使用公钥通过ssh登录。关于使用公钥自动登录ssh主机,更多可以参看这里 ssh无密码登入设置(完全版)/linux下免输入密码ssh登录

下面初始化gitosis . 要使用git用户执行下面命令

gitosis-init < [path of your id_rsa.pub]

结果大致如下

sh-4.1$ gitosis-init < id_rsa.pub
Initialized empty Git repository in /var/lib/gitosis/repositories/gitosis-admin.git/
Reinitialized existing Git repository in /var/lib/gitosis/repositories/gitosis-admin.git/
sh-4.1$ ls
gitosis  repositories

可以看到被创建的两个目录,gitosis与repositories .

运行 git clone git@localhost:repositories/gitosis-admin.git 即可把git管理配置文件的目同步到本地。需要修改配置,就在本地修改,然后git commit;git push同步到服务器上即可以即时生效了

关于gitosis资料,参考 http://linux-wiki.cn/wiki/zh-hans/%E9%85%8D%E7%BD%AEgitosis%EF%BC%88%E4%BB%A5CentOS%E4%B8%BA%E4%BE%8B%EF%BC%89

http://git-scm.com/book/zh/服务器上的-Git-Gitosis

http://git-scm.com/book/zh/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E5%9C%A8%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E9%83%A8%E7%BD%B2-Git