git push -u origin master
已有仓库?
cd existing_git_repo
git remote add origin https://gitee.com/bai-wenzhong/git-study.git
git push -u origin master
一、初始配置
#工作区(未跟踪、已跟踪【未修改/已修改】)、暂存区(已暂存)、仓库-.git目录(已提交)
#配置用户、邮箱
$ git config --global user.name "张三"
$ git config --global user.email zs@qq.com
#查看配置
$ git config --list
$ git config user.name
#帮助命令格式
$ git help <verb>
#帮助命令举例:查看config命令用法
$ git help config
二、本地仓库基础操作
#建本地git仓库,方法1:
$ git init
#建本地git仓库,方法2:
$ git clone https://gitee.com/bai-wenzhong/git-study.git
$ git clone https://gitee.com/bai-wenzhong/git-study.git newname
#查看工作区、暂存区的文件状态
$ git status
#git add 命令用法
git add .
git add *.java
git add dir1 dir2 1.txt 2.txt
根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态
#.gitignore忽略文件
有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表
我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式
文件 .gitignore 的格式规范如下:
所有空行或者以注释符号 # 开头的行都会被 Git 忽略。
可以使用标准的 glob 模式匹配。
匹配模式最后跟反斜杠(/)说明要忽略的是目录。
要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。
举例1:
$ cat .gitignore
*.[oa] #忽略所有以 .o 或 .a 结尾的文件
*~忽略所有以波浪符结尾的文件
举例2:
# 此为注释 – 将被 Git 忽略
# 忽略所有 .a 结尾的文件
*.a
# 但 lib.a 除外
!lib.a
# 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
/TODO
# 忽略 build/ 目录下的所有文件
build/
# 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
doc/*.txt
#比较工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容
$ git diff
#比较已经暂存起来的文件和上次提交的快照之间的差异
$ git diff --cached 或 git diff --staged(Git 1.6.1以上版本支持)
#将暂存区文件提交到仓库
$ git commit -m "注释。。。"
#跳过使用暂存区,直接将工作区文件提交到仓库
$ git commit -a -m "注释。。。"
#移除文件
。。。。。。
#移动文件
。。。。。。
#查看提交历史
$ git log
。。。。。。
三、远程仓库基础操作
#查看当前仓库对应的远程仓库地址
git remote -v
#在克隆完某个项目后,至少可以看到一个名为 origin 的远程库,Git 默认使用这个名字来标识你所克隆的原始仓库
#添加远程仓库,一份本地代码可以对应多个远程仓库
git remote add [shortname] [url] #远程仓库的shortname尚不存在,才可以执行添加
$ git remote add pb git://github.com/paulboone/ticgit.git
现在可以用字符串 pb 指代对应的仓库地址了。比如说,要抓取所有 Paul 有的,但本地仓库没有的信息,可以运行 git fetch pb:
$ git fetch pb
#修改仓库对应的远程仓库地址
git remote set-url origin 仓库地址
#git fetch 将远端的数据拉到本地仓库,并不自动合并到当前工作分支
如果是克隆了一个仓库,此命令会自动将远程仓库归于 origin 名下。所以,git fetch origin 会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新(或是上次 fetch 以来别人提交的更新)
$ git fetch [remote-name]
#git pull
如果设置了某个分支用于跟踪某个远端仓库的分支(参见下节及第三章的内容),可以使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支。
在日常工作中我们经常这么用,既快且好。
实际上,默认情况下 git clone 命令本质上就是自动创建了本地的 master 分支用于跟踪远程仓库中的 master 分支。
所以一般我们运行 git pull,目的都是要从原始克隆的远端仓库中抓取数据后,合并到工作目录中的当前分支。
#推送数据到远程仓库
git push [remote-name] [branch-name]
如果要把本地的 master 分支推送到 origin 服务器上(再次说明下,克隆操作会自动使用默认的 master 和 origin 名字),可以运行下面的命令:
$ git push origin master
#查看远程仓库信息
git remote show [remote-name]
$ git remote show origin
#origin url
* remote origin
URL: git@github.com:defunkt/github.git
#已同步到本地的远端分支:issues、master
Remote branch merged with 'git pull' while on branch issues
issues
Remote branch merged with 'git pull' while on branch master
master
#未同步到本地的分支:caching
New remote branches (next fetch will store in remotes/origin)
caching
#已同步到本地、在远端服务器上已被删除的分支:
Stale tracking branches (use 'git remote prune')
libwalker
walker2
Tracked remote branches
acl
apiv2
dashboard2
issues
master
postgres
#本地运行 git push 时缺省推送的远端分支:master
Local branch pushed with 'git push'
master:master
#修改某个远程仓库在本地的简称
$ git remote rename pb paul
$ git remote
origin
paul
#删除远程仓库
$ git remote rm paul
$ git remote
origin
四、本地分支-原理
#创建分支
$ git branch testing
#切换分支
$ git checkout testing
#提交一次后的效果
#切回master分支
#再次提交
五、本地分支-新建、切换、合并
#新建前
#新建并切换分支解决53号bug
$ git checkout -b iss53
等价与:
$ git branch iss53 #新建
$ git checkout iss53 #切换
#53号bug修复了一半再切回master分支修复一个紧急问题
留心你的暂存区或者工作目录里,那些还没有提交的修改,它会和你即将检出的分支产生冲突从而阻止 Git 为你切换分支。切换分支的时候最好保持一个清洁的工作区域。稍后会介绍几个绕过这种问题的办法(分别叫做 stashing 和 commit amending)。目前已经提交了所有的修改,所以接下来可以正常转换到 master
分支
$ git checkout master
#创建一个紧急修补分支 hotfix
,进行修复
$ git checkout -b 'hotfix'
$ git commit -a -m 'xxx'
#将hotfix
合并到master分支
请注意,合并时会出现“Fast forward”的提示。由于当前 master
分支所在的提交对象是要并入的 hotfix
分支的直接上游,Git 只需把 master
分支指针直接右移。换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,因为这种单线的历史分支不存在任何需要解决的分歧,所以这种合并过程可以称为快进(Fast forward)
$ git checkout master
$ git merge hotfix
#当前 hotfix
分支和 master
都指向相同的提交对象,所以 hotfix
已经完成了历史使命,可以删掉了:
$ git branch -d hotfix
#现在回到之前未完成的 #53 问题修复分支上继续工作:
$ git checkout iss53
$ git commit -a -m 'finished the new footer [issue 53]'
不用担心之前 hotfix
分支的修改内容尚未包含到 iss53
中来。如果确实需要纳入此次修补,可以用 git merge master
把 master 分支合并到 iss53
;或者等 iss53
完成之后,再将 iss53
分支中的更新并入 master
#在问题 #53 相关的工作完成之后,合并回 master
分支:
$ git checkout master
$ git merge iss53
这次,Git 会用两个分支的末端(C4 和 C5)以及它们的共同祖先(C2)进行一次简单的三方合并计算。对三方合并后的结果重新做一个新的快照,并自动创建一个指向它的提交对象(C6)。这个提交对象比较特殊,它有两个祖先(C4 和 C5)。
# iss53
也就没用了,可以删除它
$ git branch -d iss53
六、本地分支-遇到冲突时的分支合并
没有发生冲突的文件,合并后会自动提交;
存在冲突的文件,会合并,但不会提交,它会停下来等你解决冲突。要看看哪些文件在合并时发生冲突,可以用 git status
查阅。
在解决了所有文件里的所有冲突后,运行 git add
将把它们标记为已解决状态(译注:实际上就是来一次快照保存到暂存区域。)。因为一旦暂存,就表示冲突已经解决。
如果你想用一个有图形界面的工具来解决这些问题,不妨运行 git mergetool
,它会调用一个可视化的合并工具并引导你解决所有冲突。退出合并工具以后,Git 会询问你合并是否成功。如果回答是,它会为你把相关文件暂存起来,以表明状态为已解决。
再运行一次 git status
来确认所有冲突都已解决。
如果觉得满意了,并且确认所有冲突都已解决,也就是进入了暂存区,就可以用 git commit
来完成这次合并提交。
七、本地分支-管理
git branch
命令不仅仅能创建和删除分支,如果不加任何参数,它会给出当前所有分支的清单:
$ git branch
iss53
* master
testing
注意看 master
分支前的 *
字符:它表示当前所在的分支。也就是说,如果现在提交更新,master
分支将随着开发进度前移。若要查看各个分支最后一个提交对象的信息,运行 git branch -v
:
$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes
要从该清单中筛选出你已经(或尚未)与当前分支合并的分支,可以用 --merge
和 --no-merged
选项(Git 1.5.6 以上版本)。比如用 git branch --merge
查看哪些分支已被并入当前分支(译注:也就是说哪些分支是当前分支的直接上游。):
$ git branch --merged
iss53
* master
之前我们已经合并了 iss53
,所以在这里会看到它。一般来说,列表中没有 *
的分支通常都可以用 git branch -d
来删掉。原因很简单,既然已经把它们所包含的工作整合到了其他分支,删掉也不会损失什么。
另外可以用 git branch --no-merged
查看尚未合并的工作:
$ git branch --no-merged
testing
它会显示还未合并进来的分支。由于这些分支中还包含着尚未合并进来的工作成果,所以简单地用 git branch -d
删除该分支会提示错误,因为那样做会丢失数据:
$ git branch -d testing
error: The branch 'testing' is not an ancestor of your current HEAD.
If you are sure you want to delete it, run 'git branch -D testing'.
不过,如果你确实想要删除该分支上的改动,可以用大写的删除选项 -D
强制执行,就像上面提示信息中给出的那样。
八、远程分支-原理
1.假设你们团队有个地址为 git.ourcompany.com
的 Git 服务器。
如果你从这里克隆:
1)Git 会自动为你将此远程仓库命名为 origin
,并下载其中所有的数据,建立一个指向它的 master
分支的指针,在本地命名为 origin/master
(远程仓库名)/(分支名),但你无法在本地更改其数据。
2)Git 建立一个属于你自己的本地 master
分支,始于 origin
上 master
分支相同的位置,你可以就此开始工作
2.如果你在本地 master
分支做了些改动,与此同时,其他人向 git.ourcompany.com
推送了他们的更新,那么服务器上的 master
分支就会向前推进,而于此同时,你在本地的提交历史正朝向不同方向发展。不过只要你不和服务器通讯,你的 origin/master
指针仍然保持原位不会移动
3.可以运行 git fetch origin
来同步远程服务器上的数据到本地。该命令首先找到 origin
是哪个服务器(本例为 git.ourcompany.com
),从上面获取你尚未拥有的数据,更新你本地的数据库,然后把 origin/master
的指针移到它最新的位置上
4.为了演示拥有多个远程分支(在不同的远程服务器上)的项目是如何工作的,我们假设你还有另一个仅供你的敏捷开发小组使用的内部服务器 git.team1.ourcompany.com
。可以用 git remote add
命令把它加为当前项目的远程分支之一。我们把它命名为 teamone
,以便代替完整的 Git URL 以方便使用
5.现在你可以用 git fetch teamone
来获取小组服务器上你还没有的数据了。由于当前该服务器上的内容是你 origin
服务器上的子集,Git 不会下载任何数据,而只是简单地创建一个名为 teamone/master
的远程分支,指向 teamone
服务器上 master
分支所在的提交对象 31b8e
九、推送本地分支到远程分支
1.如果你有个叫 serverfix
的分支需要和他人一起开发,可以运行 git push (远程仓库名) (分支名)
:
$ git push origin serverfix
若想把远程分支重命名,可以用 git push origin serverfix:newname
来推送数据。
2.接下来,当你的协作者再次从服务器上获取数据时,他们将得到一个新的远程分支 origin/serverfix
,并指向服务器上 serverfix
所指向的版本
$ git fetch origin
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 15 (delta 5), reused 0 (delta 0)
Unpacking objects: 100% (15/15), done.
From git@github.com:schacon/simplegit
* [new branch] serverfix -> origin/serverfix
值得注意的是,在 fetch
操作下载好新的远程分支之后,你仍然无法在本地编辑该远程仓库中的分支。换句话说,在本例中,你不会有一个新的 serverfix
分支,有的只是一个你无法移动的 origin/serverfix
指针。
3.如果要把该远程分支的内容合并到当前分支,可以运行 :
$ git merge origin/serverfix
4.如果想要一份自己的 serverfix
来开发,可以在远程分支的基础上分化出一个新的分支来:
$ git checkout -b serverfix origin/serverfix
十、跟踪远程分支
从远程分支 checkout
出来的本地分支,称为 跟踪分支 (tracking branch)。跟踪分支是一种和某个远程分支有直接联系的本地分支。在跟踪分支里输入 git push
,Git 会自行推断应该向哪个服务器的哪个分支推送数据。同样,在这些分支里运行 git pull
会获取所有远程索引,并把它们的数据都合并到本地分支中来。
在克隆仓库时,Git 通常会自动创建一个名为 master
的分支来跟踪 origin/master
。这正是 git push
和 git pull
一开始就能正常工作的原因。当然,你可以随心所欲地设定为其它跟踪分支,比如 origin
上除了 master
之外的其它分支。刚才我们已经看到了这样的一个例子:git checkout -b [分支名] [远程名]/[分支名]
。如果你有 1.6.2 以上版本的 Git,还可以用 --track
选项简化:
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "serverfix"
要为本地分支设定不同于远程分支的名字,只需在第一个版本的命令里换个名字:
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch refs/remotes/origin/serverfix.
Switched to a new branch "sf"
现在你的本地分支 sf
会自动将推送和抓取数据的位置定位到 origin/serverfix
了。
十一、删除远程分支
如果不再需要某个远程分支了,可以用这个非常无厘头的语法来删除它:git push [远程名] :[分支名]
。如果想在服务器上删除 serverfix
分支,运行下面的命令:
$ git push origin :serverfix
To git@github.com:schacon/simplegit.git
- [deleted] serverfix
咚!服务器上的分支没了。你最好特别留心这一页,因为你一定会用到那个命令,而且你很可能会忘掉它的语法。有种方便记忆这条命令的方法:记住我们不久前见过的 git push [远程名] [本地分支]:[远程分支]
语法,如果省略 [本地分支]
,那就等于是在说“在这里提取空白然后把它变成[远程分支]
”。
十二、分支的衍合-rebase
把一个分支中的修改整合到另一个分支的办法有两种:merge
和 rebase
(衍合)
十三、基本的衍合操作
回顾分支合并,之前介绍过,最容易的整合分支的方法是 merge
命令,它会把两个分支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)进行三方合并,合并的结果是产生一个新的提交对象(C5)
其实,还有另外一个选择:你可以把在 C3 里产生的变化补丁在 C4 的基础上重新打一遍。在 Git 里,这种操作叫做衍合(rebase)。有了 rebase
命令,就可以把在一个分支里提交的改变移到另一个分支里重放一遍。
在上面这个例子中,运行:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
它的原理是回到两个分支最近的共同祖先,根据当前分支(也就是要进行衍合的分支 experiment
)后续的历次提交对象(这里只有一个 C3),生成一系列文件补丁,然后以基底分支(也就是主干分支 master
)最后一个提交对象(C4)为新的出发点,逐个应用之前准备好的补丁文件,最后会生成一个新的合并提交对象(C3'),从而改写 experiment
的提交历史,使它成为 master
分支的直接下游