备份的重要性
很多人对于版本控管系统的刻板印象都是认为只有软件开发那种高科技领域才用得到,可是事实上在很多中小型的开发团队中,版本控管也不见得被应用在开发流程中。有不少的开发人员甚至还是使用人工的方式在做备份,例如:将每天的工作成果以文件夹为单位备份并使用日期做为文件夹名称以协助辨识,更有不少人是完全不做备份、交由老天来决定。
而生活总是充满变化与挑战的,先不说工作的成果有可能因为一个意外就一瞬间化为乌有,在工作的过程中就有可能会面临很多的选择与决定。每一个决定都可能会对最终的成果造成重大的影响、左右成败,但不见得在每一个抉择的当下都有足够的经验来判断哪一个决定对结果是好的。
就像 Nicolas Cage 主演的电影 Next 中,剧情后段寻找炸弹的那个场景特效一样,寻找炸弹的路径有很多条,每一条路径又有很多个分歧点,产生很多不同的可能性。可惜的是我们并没有像剧中的主角有预知能力,没有办法选择一条可以产出最好的结果路径。
不过,真的不行吗?的确我们不具有预知的能力,但是我们却可以在事后发觉当初的决定不恰当时,回到决定的那个路径点重新做选择,这就是版本控管的概念。
所以并<u>不是只有软件开发的工作才有需要应用版本控管系统</u>,只要是工作产出有涉及到电子文件的保存,就应该要考虑使用版本控管系统来保障工作的成果。有了版本控管系统在工作出现抉择时,相对要承受的心理压力也会小了许多,因为就算选定的结果不如预期还是有个备援方案可以简单地重新做出选择。
以程序开发的一段过程来做为例子:假设有一段写好了的源代码,但后来发现有效能上的问题,想要针对这个问题调整程序。一来心理上会舍不得目前的成果,二来也许新的源代码并不是那么有把握可以改善效能、或是不确定会对其他的功能产生什么冲击。这时如果可以先把整个源代码现在的状态备份下来,就可以毫无顾忌地进行构想中的修改,一旦出现了什么问题还可以再还原回来、做不同的尝试。
当然如果需求只有备份,前面提到的人工方式也没有什么不好,那使用版本控管系统的诱因是什么?就像前面提到的情境,到终点前的分歧点永远不会只有一个,文件结构的确也可以应付树状分支的备分结构,在管理上却是人力和时间的不断投入。既然已经<u>有现成的系统可以协助,为什么不使用系统、让时间和精力投注在更有价值的事务上?</u>
有开发过 Native iOS 应用程序的人应该都知道 Xcode 有提供 Snapshot 的功能,我觉得这个功能很实用,我在其他的开发工具上还没有看过。当我遇到前面举例的情况时,就会使用 Snapshot 把源代码的状态备份起来,在持续工作的过程中如果想要参照原本程序的写法,还可以使用差异比对的功能来检视问题。但由于每一次 Snapshot 的项目是没有前后关系,当情况变得复杂时就会出现追踪不易的问题,会被强迫由原本的思绪中抽离、以进行一些管理的动作,在效率上来说并不是很理想。所以 Snapshot 大多只是应用在小幅度的修改,或是用来尝试不熟悉的函式,大幅度的修改还是会回到版本控管的机制中处理。
数据库系统的 Transaction Log 机制也是一个例子,由于 Transaction Log 是把对数据库内数据的变化做循序式的记录,跟人工备份的模式差不多。一旦出现人为操作的失误导致数据异常,通常 DBA 都会先问清楚发生的时间点,再把数据库由上次全备份的状态以 Transaction Log 还原到附近的时间点,再逐步的套用 Log 中的指令,以排除有问题的数据修改。像这样一次只发生一个操作失误还可以应付,一旦是多重的失误要由 Transaction Log 来回复到期望的状态就不是件简单的事了。
为什么选择 Git
在早期版本控管的系统大多定位在多人协同合作,都是以 Centralized 的模式为主,一定要有一台提供版本控管的服务器才能运作,这应该也是一般人望之却步的原因。然而,随着 DVSC 概念的兴起,以目前最热门的 Git 功能来看,要应用在个人的工作版本管理上似乎就不再那么遥不可及。
不过平心而论,以 Git 的亲和度对很多非科技从业人员来说算不上是友善。在操作上大部份还是以指令输入为主,图形介面的选择及整合上还是有很大的空间。如果非科技的从业人员觉得自己的工作成果真的很重要,想要使用 Git 来做为版本控管的机制,可能要有会投入一段不算短学习时间的心理准备。所幸 Git 的资源还算丰富,甚至还有翻译好的中文教学文件。
Git 的运作概念
在学习 Git 的运作过程中,由于过去一直都是使用 Centralized 的版本控管系统,花了一点时间来适应和转换原本的观念。Git 记录版本的模式是根据操作人员的指示,把特定时间点的所有文件内容保存下来形成一个个的 Commit,并且依据产生的先后关系串成一条一条的 Commit 链。
每一个 Commit 链就像之前提到的电影特效,是把你所尝试过的路径记录下来。但也不要想得太过美好,记录的时间点是由操作的人决定并不是即时的,即便是使用 OS X 的 Time Machine 来做备份也是有时间差的,并不是无段式的。有在玩 RPG 的人,就把 Commit 当做是存档来看就行了。有做存档,万一角色挂了或卡关才有办法召唤 S/L 大法。
换个说法,想像你目前正在一个巨大迷宫的入口,而你手上可以利用的只有绳子。想要顺利的走出迷宫可以做的就是:在迷宫的入口处绑上绳子的一头,绳子其他部份留在手上、边走边放,做为走过路径的记录。迷宫很大、岔路很多,你不想试完每一种可能,所以你就使用你的第六感、在你觉得有可能的分岔点于绳子上打个结个为记录,以方便之后回来做不同方向尝试的起点,这个结在 Git 的术语里就是 Commit。但每一个结都长的一样,怎么区分?所以你可能会想要在结上贴个贴纸,写个简单的说明,这个就是 Tag。
正常的情况之下,如果走到死胡同,则会再循线回到最近的一个有结的岔路口,在岔路口用另一条绳子绑在原本的绳子上,用同样的方式留下走过的记录,直到碰到另一个死胡同再重复之前的程序。在岔路分出一条不同的线的动作就叫 Branch,如果岔路之后不是走到死胡同,而是遇到之前放的另一条线则是叫 Merge。
如果你突然心灵福至,觉得之前的某一个岔路有可能走出迷宫,于是你想先暂时放下目前这条线的探索,试试看另一条线。几次之后发现了一个意想不到的情况,每一条线长得都差不多,已经开始分不清楚哪一条线是哪一条。所以必须要在每一条线的最后一个绳结贴上贴纸。每一条线的最末端增加了一个结,就把贴纸移到新的绳结上,用来代表每一个分支的最新状态。这种贴纸在 Git 中就是 Branch,<u>** Git 所谓的 Branch 就数据的设计来看,严格来说并不是指一整条线,是某一个时间点的文件内容状态**</u>。同时 Git 会设定其中一条线的名字是 master,做为初始时预设产生的 Branch。如果要问为什么贴纸一定要贴在绳结上?别忘了,之前提过 Git 还没有神奇到可以提供无段式的数据存档快照,所以有记录才能做关连,而切换分支的这个动作在 Git 中称为 checkout。
刚才有提到二条以上的线可能会 Merge 到同一个绳结上,如果这个绳结又正好是最后一个,以之前一段说明的逻辑,这个绳结上就会有二个以上用来表示分支的贴纸,当然还可能会有自订、不是用来表示分支的贴纸。像这种代表分支的贴纸在 Git 中是由系统控制的,Git 会替我们决定要把贴纸移到哪一个绳结上。另外还有一个特别的贴纸叫 HEAD,是用来表示目前工作中文件内容状态的 Commit 位置。如果 Git 只是单纯地把贴纸由前一个绳结移到新的绳结上,术语上叫 Fast forward,顾名思义就是在没有更动分支与 Commit 的结构下,快速的推进了版本记录。
在先前有提到 Branch 和 Tag 都是用来指向某一个 Commit,在 Git 中 Commit 是由 SHA1 所产生的字串用来当做唯一识别码,很多的 Git 指令参数都可以自动识别所输入的参数值是 Branch、Tag 或是唯一识别码,并指向对应的 Commit。由于唯一识别码很长,在用来做为参数时可以不用全部输入,只要输入开头的断个字节即可,例如只打前四个字节。
在阅读教学文件以了解 Git 的运作逻辑的过程中,看到很多用来表示 Repository 状态的图例,像这个。一开始以为箭头标示的方向是时间演进的历程,所以在看图理解文字意义时会有些障碍。其实并不是,后来才发现跟 UML 的类别图概念比较接近,是以程序数据结构的角度,用来表达每一个 Commit 的内容是源自于哪一个 Commit。
除了之前提到的概念之外,还有一个概念是之后在操作 Git 时会很频繁地使用到,那就是 Staging Area。在看完文件的说明之后,感觉上这个过程像购物网站的购物车,当你对某个商品有兴趣时,如果只是勾选起来并选好数量,会发现其实还不能结帐。这时必须要先把想要买的商品加到购物车里,购物车就是 Git 里的 Staging Area。同样地,当你觉得你想要加购商品,你得再把加购的数据送到购物车里。所以,当有对文件进行修改后,要使用 add 指令将文件加入 Staging Area 中。而此时下 commit 指令所保留的是你执行 add 时当下的文件,而不是最后的版本。也就是如果你 add 完之后,又再次修改同个文件,commit 出去的文件是 add 指令前的版本。
以上说明的内容只是用来便于理解 Git 的运作概念,如果有去阅读过阅读教学文件第 10 章的内容,会发现实际的情况和绳结跟贴纸的说法上还是有差距的。
Git 的备份规划与环境设置
接下来要开始根据 Git 的特性做一些备份环境的规划,首先是用来存放 Git 的 Repository 位置,为了简化备份的程序,所有的工作文件都规划放在同一个文件夹下,例如:Workspace。不同工作内容则可以使用 Branch 来做区隔,例如:daily/work1、daily/work2。这样的做法可以避免设定太多的 Repository,在管理工作上不会太过复杂,也不用特别记录什么工作的 Repository 在哪个文件夹下。做 Daily Backup 时也只需要在一个 Repository 下 Commit 的指令,就可以把一天工作的成果给保留下来。
就算是一天之中在不同的 Branch 间切换进行不同的工作,由于 Git 的特性,切换 Branch 前会先要求你清空 Staging Area 的内容,不管是 Commit 或放弃修改的内容,所以不致于会漏了做备份。如果不是工作告一段落,只是想要暂时切换到另的 Branch,待会又要再切回来,Git 也有提供对应的功能。
使用的指令示范如下:
-
git stash
会先把 Staging Area 中修改过的内容暂存下来。 -
git stash list
列出被暂存的清单。 -
git stash pop
把最后一次暂存的内容再还原回 Staging Area。 -
git stash apply
取出最后一次的暂存数据. 但是数据不移除。 -
git stash clear
把所有暂存的数据都清除掉。 -
git reset HEAD <filename>
把指定的文件从 Staging Area 状态更改回 Unstaging 的状态,如果文件还没 Commit 过则会是 Untracked,但是文件内容仍然保持修改后的样子。 -
git checkout <filename>
把在 Unstaging 状态的指定文件内容还原到最后 Commit 的样子。 -
git checkout -f
将所有文件都以最后一次 Commit 的版本覆盖,有修改的内容都会被还原。 -
git checkout <commit ref> .
将所有文件都以指定 Commit 的版本覆盖,同样地,有修改的内容都会被还原。如果使用 Tag 来指定 Commit,且不是 Branch 上最后的 Commit, 会出现 "detached HEAD" 的讯息,此时所取得的内容为快照,是不能更改文件的内容。如果要更改,必须要在这个 Commit 上建立一个 Branch。
当然这样的规划是以个人备份的角度来考量,由于不同的工作专案都混同一个 Repository 里,当所有的工作 merge 回 master branch 时会让 master branch 的版本控管变得很复杂。所以多人协同合作要进行版本控管时,并不建议使用这样的规划,还是要依据专案的特性与分工上的需求来审慎地规划 Repository。
决定好了 Repository 的位置后接下来就是要先对 Repository 做初始化的工作,只要切换到预计做为 Repository 的文件夹,并且执行以下指令即可完成初始化的工作:
git init
使用的作业系统没有内建 Git 时,要先进行安装的程序,相关的资讯可以参考教学文件中的说明。
如果 Repository 所在的文件夹于就初始化之前已经有文件了,可以先做一次 Commit,指令如下所示:
git add .
git commit -m 'initial commit'
但是在 Commit 之前如果有一些程序产生的暂存档,像是 Word 会在编辑时会产生一些 ~ 开头档名的文件、或是编译程序时会产生很多的中介档,应该要先按照教学文件的说明把文件给预先排除掉。
这项功能主要是用在工具所产生的暂存档,因为这类型的文件变动很频繁、被删除了并不影响工作。如果将这类的文件列入版本控管,会有大量增加储存空间的疑虑。像是开发工具在编译文件时会出现很多中介档,删掉并不影响开发工作的进行,只要再重新编译文件就会再产生。所以这类的文件最好是列入忽略清单中,以有效控制 Repository 的大小。不过,需注意的是要被忽略的文件,但是如果过滤条件是在被 Git 列入追踪之后才加入,就算符合设定的条件,文件并不会被自动解除追踪、要手动移除,所以前一段才会提到要在 Commit 之前进行。
如果真的有文件在 Checkin 之后,不希望再被 Git 追踪,可以使用以下的指令来达成:
git rm --cached <file name>
git rm -r --cached <folder name>
以下的指令可以显示被 Git 所追踪的文件清单:
git ls-tree --name-only -r HEAD
如果以上的指令所显示的中文档名出现奇怪的编码时,可以使用以下的指令来尝试解决问题:
git config --global core.quotepath false
制作 .gitignore
的细节可以参考官方网站的说明,如果把 .gitignore
的文件放在 Repository 的文件夹下,则这个设定的内容可以被 Checkin 并 Push 到远端与同一个工作小组的成员共用,但只影响这个 Repository。如果要让所有 Repository 都套用同一个设定,可以使用以下的指令:
git config --global core.excludesfile ~/.gitignore_global
以上示范的指令中,.gitignore_global
是共用设定的文件名称,名字可以自行调整,共用设定一般的习惯位置都是放在登入帐号的 Home 路径之下。
由于 .gitignore
在内容会随著作业系统平台、使用的电子档编辑工具而需要做不同的设定,尤其是在进行程序开发工作时经常会需要跨语言和工具。然而,要收集每一个语言编译器或工具使用的暂存档是一项很耗时的工作,而一般的情况下很少会去这么深入的了解。所幸在 GitHub 上有提供了范本可供参考,在第一层列出了很多程序语言需要的设定,在 Global 的文件夹下则是列出了相关工具及作业系统平台所需的设定。
另外还有一种情况是有些文件希望被列入追踪,但是又不希望被异动并上传。像是应用在小组共用的初始设定,成员在 Clone 可以取得并且去修改,但所修改的内容并不会被 Commit。如此可以保障当新成员加入时,会使用小组预设的状态或环境、减少新手问题,等新成员熟悉之后就可以自行依习惯调整,但又不会影响初始设定。
-
git update-index --assume-unchanged <file name>
会让 Git 忽略指定的文件被修改的结果,并且不会被显示在 git status 或 git diff 指令结果中,更不会被 Commit。 -
git update-index --no-assume-unchanged <file name>
让指定的文件重新列入到被追踪的范围。
Git 的日常操作
完成了以上的设定就可以开始使用 Git 做为日常工作产出的备份机制,但是在进行任何的文件编辑工作之前应该先建立并切换到 Branch,例如:daily,避免在 master 上进行。主要的考量是在于每天的备份会产生一到多个的 Commit,全部都串在 master 上会让 master 的历史记录显得很庞杂。所以如果把每天备份产生的 Commit 集中到另一个 Branch 去,等到有可以交付的版本再 Merge 回 master,相对的来说 master 的历史记录会比较有管理上的优势。例如:今天客户需要之前某次交付的文件内容,就可以切回到 master 快速地在精简的记录中找到需要的 Commit。如果客户还想要针对这个版本做调整,也可以直接建立出 Branch 以因应修改的需求。
如果 Git 是要用来做程序开发工作的备份,可以参考 “A successful Git branching model” 这篇文章的说明。以我的工作经验来看,这篇文章中所做的 Branch 规划已经可以满足大部份在程序开发的过程中会出现的情境。
产生 Branch 指令如下:
-
git branch <branch name> [<commit ref>]
产生一个新的 Branch,如果有提供第二个参数,会使用第二个参数所代表的 Commit 来产生 Branch,否则就使用目前的 Commit。 -
git checkout -b <branch name> [<commit ref>]
和第一个一样,不同的是第二个指令在产生完 Branch 之后会立刻切换到所新增的 Branch。
产生 Branch 时发现名字打错了,可以使用以下的指令来进行修改:
git branch -m [<old name>] <new name>
如果觉得使用 Branch 太过复杂了,只想单纯的使用 master 来做每天的备份,但是又想要有一个方法可以识别 Commit 的内容。这时候 Tag 就可以派上用场,当然也不是只有这个情境下才可以使用。在 Git 中 Tag 有二种类型:Lightweight 和 Annotated。Lightweight 照字面的意思就是轻量化的版本,只能输入一串文字做为名称,例如:v1.0、v2.3。而 Annotated 就可以有比较完整的内容,名称、Email、日期和额外的附注内容。官网是建议使用 Annotated 类型的 Tag,因为在多人协同工作时可确保提供足够的资讯。但是用来做为个人的备分工具,其实可以使用轻量化的版本就行了。
以下列出 Tag 相关的管理指令:
-
git tag
可以列出目前可查询的 Tag 清单。 -
git tag -l <filter>
可显示符合指定条件的 Tag 清单。 -
git tag <tag name>
可以产生一个 Lightweight 的 Tag 在目前的 Commit 上。 -
git tag -a <tag name> -m <tag message>
可以产生一个 Annotated 的 Tag 及对应的注释内容。 -
git tag -a <tag name> -m <tag message> <commit ref>
可以在指定的历史 Commit 中新增 Tag 及对应的注释内容。 -
git tag -s <tag name> -m <tag message>
可以产生一个签名过的 Annotated Tag 及对应的注释内容。 -
git tag -v <tag name>
可以验证 Tag 签名的正确性,执行时会需要有签署者的公开金钥。 -
git tag -d <tag name>
删除本地指定名称的 Tag。 -
git tag <tag name> <name of other branch>
会在指定的 Branch 中最后一个 Commit 上产生 Tag。
想要查询曾经 Commit 的记录,可以使用以下的指令:
-
git log
列出与目前 Branch 相关的 Commit 记录。 -
git log --all
列出所有的 Commit 记录。
*git log --stat --summary
在 git log 显示的资讯上,加列每个版本间的更动文件和行数。 -
git log <file name>
只列出有包含指定文件被异动的 Commit 清单。 -
git log <directory>
只列出有包含指定文件夹被异动的 Commit 清单。 -
git log --since="2 weeks ago"
只列出最后这 2 周的 Commit 清单。 -
git log --pretty=oneline
列出 Commit 清单时,每一个 Commit 只使用一行来显示资讯。 -
git log --pretty=format:'%h was %an, %ar, message: %s'
列出 Commit 清单时,每一个 Commit 以自订的字串样式来显示资讯。 -
git log --pretty=format:'%h : %s' --graph
列出 Commit 清单时,会有简单的文字图形化来呈现分支的状态。 - git log --pretty=format:'%h : %s' --topo-order --graph`
列出 Commit 清单时,依照主分支排序。 -
git log --pretty=format:'%h : %s' --date-order --graph
列出 Commit 清单时,依照时间排序。
要检视 Commit 中所保存的文件内容,可以使用以下的指令:
-
git show <commit ref>[:<file name>]
显示指定 Commit 的文件内容,如果有指定文件名称则只显示指定文件的内容。 -
git show HEAD
显示后最 Commit 的文件内容。 -
git show HEAD^
显示后最 Commit 前一个版本的文件内容。 -
git show HEAD^^
显示后最 Commit 前二个版本的文件内容。 -
git show HEAD~4
显示后最 Commit 前四个版本的文件内容。
当有需要对比对不同版本间的差异时,可以使用以下的指令:
-
git diff
显示工作中文件与 Staging Area 间的差异。 -
git diff --cached
显示 Staging Area 与最后 Commit 结果间的差异。 -
git diff <commit ref> <commit ref>
显示二个指定 Commit 间的差异。 -
git grep "hello" <commit ref>
搜寻指定 Commit 中所有的文件内容中是否有 hello 字串。
在工作告一段落需要有一个可以交付的版本时,可以使用以下的指令把最新的 Commit 合并回 master 去:
git checkout master
git merge --no-ff daily
git tag v1.0
如果在 Merge 的时候没有使用 --no-ff
的参数,以之前规划的模式,会让 master 直接指向 daily 的最后一个 Commit,这并不是原本设计想要的结果。因为 Merge 之后的 Branch 结构就跟一开始没有分出 Branch 的结果相同,只会有一条 Branch。使用了 --no-ff
的参数,会让新增加出来的 Commit 分别指向 daily 的最后一个 Commit 及 Merge 前 master 所在的 Commit,如此 Branch 的结构上就会形成二条线。
或是觉得在工作告一段落之后,先前的每日备份已经没有保留的必要,可以使用以下的指令来让所有的 Commit 被压缩成一个:
git merge --squash daily
当想要挑选某几天备份的内容来整合,可以使用以下的指令:
git cherry-pick <commit ref>
如果要挑选多个 Commit 合并为一个,可以加上 -n 指令。在执行指令时 Git 就不会先做 Commit,这样可以不断挑选要合并的 Commit,最后再以 git commit 指令送入 Repository 即可。
进行远端 Repository 同步
使用 Git 来进行每日备份,虽然可以用来保障每天的工作成果,但还是有一定的风险存在着。毕竟所有的数据还是放在同一个磁碟里,一旦磁碟出现问题,所有的数据一样会消失不见。所以如果可以有一个远端的 Repository 把 Commit 给推送过去,保障就可以再多加一层。
以下指令可以为 Repository 增加一个远端 Repository 的连结资讯,并且进行上传的工作:
git remote add origin git@remotehost:/project.git
git push origin master
'origin' 是 Git 预设的远端名称,每一个 Repository 都可以多个远端的 Repository 建立连结资讯,并且给定一个字串做为识别。如果在 Push 时没有指定哪一个远端 Repository,Git 会预设代人号 origin 的连结资讯。
管理远端 Repository 的相关指令如下:
-
git remote show
显示目前已设定连结资讯的远端 Repository。 -
git remote update
更新所有远端 Repository 的 Branch 资讯。 -
git branch -r
列出所有远端 Repository 的 Branch 清单。
在指定要 Push 的 Branch 资讯时,标准的格式是 refs/heads/<branch name> 但可以省略为 <branch name>。同时 Push 时也需要指定本地和远端的 Branch 资讯并以冒号分隔,如果名称一样也有省略的方式。所以以下的三个指令会达成相同的效果:
git push origin refs/heads/master:refs/heads/master
git push origin master:master
git push origin master
要从远端下载 Commit 的资讯,可以使用以下的指令:
git fetch origin
同样地,如果不指定要下载哪一个远端的 Repository,会使用预设的 origin。下载所取得的远端 Branch 不会自动在本地端建立,要使用 checkout 来建立,像以下所示范的指令。如果本地端已经有对应的 Branch 则要以 Merge 的程序把 Commit 整合到本地的 Repository 中。
git checkout -b daily origin/daily
另外还有一个指令可以在下载后,于没有冲突的情况下,一并对有对应关系的 Branch 执行 Merge 的程序,指令示范如下:
git pull origin
想要删除远端的 Branch,可以参考以下示范的指令:
git push origin :daily
有使用 Tag 来识别 Commit 时需要特别注意,Tag 预设是不会被 Push 到远端。以下是 Tag 要 Push 到远端的相关指令:
-
git push origin --follow-tags
在 Push 的同时,一并将关连到的 Tag 上传到远端。 -
git push origin : <tag name>
可以把指定的 Tag 给 Push 到远端的 Repository 去。 -
git push origin --tags :
一次 Push 所有远端不存在的 Tag 到远端去。 -
git push origin :refs/tags/<tag name>
删除远端的 tag。 -
git fetch origin —-tags
一次下载所有远端的 Tag。
如果嫌每次 Push 都要记得下参数太麻烦,可以修改 Repository 里 .git/config 文件的 remote 资讯为以下的内容:
[remote "origin"]
url = ...
fetch = +refs/heads/*:refs/remotes/origin/*
push = +refs/heads/*
push = +refs/tags/*
修改过后,只要执行 git push origin
就可以把所有的 Branch 和 Tag 强制上传,但如果只要传特定的内容则要在参数中指定名称把以上的设定覆写掉。这个方法是在做个人工作备份的情境下使用,在多人分工的情境下并不建议使用,因为很容易不小心就把临时性的 Branch 上传,以致弄乱了 Server 上的 Repository 内容。
没有执行过以下的指令,每次输入 git push origin
都会出现警告讯息。在指令中 push.default
被设定为 matching 时会把二边都存在的同名 Branch 全部进行同步,如果是 simple 则只会同步目前所在的 Branch。
git config --global push.default matching