5. Git的基本操作
Git 基本操作
Git 的工作就是创建和保存你项目的快照及与之后的快照进行对比。
本章将对有关创建与提交你的项目快照的命令作介绍。
Git 常用的是以下 6 个命令:git clone、git push、git add 、git commit、git checkout、git pull,后面我们会详细介绍。
说明:
- workspace:工作区
- staging area:暂存区/缓存区
- local repository:版本库或本地仓库
- remote repository:远程仓库
一个简单的操作步骤:
$ git init
$ git add .
$ git commit
- git init - 初始化仓库。
- git add . - 添加文件到暂存区。
- git commit - 将暂存区内容添加到仓库中。
创建仓库命令
下表列出了 git 创建仓库的命令:
命令 | 说明 |
---|---|
git init | 初始化仓库 |
git clone | 拷贝一份远程仓库,也就是下载一个项目。 |
$ mkdir runoob
$ cd runoob/
$ git init
$ git clone https://github.com/tianqixin/runoob-git-test
$ cd simplegit/
$ ls
README.md runoob-test.txt test.txt
要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone
命令克隆。
Git支持多种协议,包括https
,但ssh
协议速度最快。
使用https
除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh
协议而只能用https
。
提交与修改
Git 的工作就是创建和保存你的项目的快照及与之后的快照进行对比。
下表列出了有关创建与提交你的项目的快照的命令:
命令 | 说明 |
---|---|
git add | 添加文件到暂存区 |
git status | 查看仓库当前的状态,显示有变更的文件。 |
git diff | 比较文件的不同,即暂存区和工作区的差异。 |
git commit | 提交暂存区到本地仓库。 |
git reset | 回退版本。 |
git rm | 删除工作区文件。 |
git mv | 移动或重命名工作区文件。 |
新增与提交
#添加当前目录下的所有文件到暂存区
$ git add .
#添加指定目录到暂存区,包括子目录:
$ git add [dir]
#添加一个或多个文件到暂存区:
$ git add [file1] [file2] ...
#通常我们使用 -s 参数来获得简短的输出结果。
$ git status -s
#-a 参数设置修改文件后不需要执行 git add 命令,直接来提交[message] 可以是一些备注信息。
$ git commit -a -m 'message'
$ git commit -m 'message'
我们可以用 --oneline 选项来查看历史记录的简洁的版本。
$ git log --oneline
版本回退
--mixed 为默认,可以不用带该参数,用于重置暂存区的文件与上一次的提交(commit)保持一致,工作区文件内容保持不变。
$ git reset HEAD^ # 回退所有内容到上一个版本
$ git reset HEAD^ hello.php # 回退 hello.php 文件的版本到上一个版本
$ git reset 052e # 回退到指定版本
--hard 参数撤销工作区中所有未提交的修改内容,将暂存区与工作区都回到上一次版本,并删除之前的所有信息提交:
$ git reset –-hard HEAD~1 # 回退上一个版本
$ git reset –-hard bae128 # 回退到某个版本回退点之前的所有信息。 版本号没必要写全,前几位就可以了,Git会自动去找。
$ git reset --hard origin/master # 将本地的状态回退到和远程的一样
说明:现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id
怎么办?
在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^
回退到add distributed
版本时,再想恢复到append GPL
,就必须找到append GPL
的commit id。Git提供了一个命令git reflog
用来记录你的每一次命令:
$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file
HEAD 说明:
- HEAD 表示当前版本
- HEAD^ 上一个版本
- HEAD^^ 上上一个版本
- HEAD^^^ 上上上一个版本
可以使用 ~数字表示
- HEAD~0 表示当前版本
- HEAD~1 上一个版本
- HEAD^2 上上一个版本
- HEAD^3 上上上一个版本
- 以此类推…
管理修改
#显示暂存区和工作区的差异:
$ git diff [file]
Git是如何跟踪修改的,每次修改,如果不用git add
到暂存区,那就不会加入到commit
中。
撤销修改
你可以发现,Git会告诉你,git checkout -- file
可以丢弃工作区的修改:
$ git checkout -- readme.txt
命令git checkout -- readme.txt
意思就是,把readme.txt
文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt
自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt
已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit
或git add
时的状态。
现在,看看readme.txt
的文件内容:
$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
文件内容果然复原了。
git checkout -- file
命令中的--
很重要,没有--
,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout
命令。
现在假定是凌晨3点,你不但写了一些胡话,还git add
到暂存区了:
$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
My stupid boss still prefers SVN.
$ git add readme.txt
庆幸的是,在commit
之前,你发现了这个问题。用git status
查看一下,修改只是添加到了暂存区,还没有提交:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.txt
Git同样告诉我们,用命令git reset HEAD <file>
可以把暂存区的修改撤销掉(unstage),重新放回工作区:
$ git reset HEAD readme.txt
Unstaged changes after reset:
M readme.txt
git reset
命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD
时,表示最新的版本。
再用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
还记得如何丢弃工作区的修改吗?
$ git checkout -- readme.txt
$ git status
On branch master
nothing to commit, working tree clean
整个世界终于清静了!
现在,假设你不但改错了东西,还从暂存区提交到了版本库,怎么办呢?还记得版本回退一节吗?可以回退到上一个版本。不过,这是有条件的,就是你还没有把自己的本地版本库推送到远程。还记得Git是分布式版本控制系统吗?我们后面会讲到远程版本库,一旦你把stupid boss
提交推送到远程版本库,你就真的惨了……
- 小结
又到了小结时间。
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file
。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>
,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
删除文件
以下实例从暂存区和工作区中删除 runoob.txt 文件:
$ git rm runoob.txt
如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f。
强行从暂存区和工作区中删除修改后的 runoob.txt 文件:
$ git rm -f runoob.txt
可以递归删除,即如果后面跟的是一个目录做为参数,则会递归删除整个目录中的所有子目录和文件:
$ git rm –r *
$ git commit -m "remove"
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git checkout -- test.txt
git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”
命令git rm
用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
提交日志
命令 | 说明 |
---|---|
git log | 查看历史提交记录 |
git blame <file> | 以列表形式查看指定文件的历史修改记录 |
远程操作
命令 | 说明 |
---|---|
git remote | 远程仓库操作 |
git fetch | 从远程获取代码库 |
git pull | 下载远程代码并合并 |
git push | 上传远程代码并合并 |
显示所有远程仓库:
$ git remote -v
显示某个远程仓库的信息:
$ git remote show [remote]
添加远程版本库:
git remote add [shortname] [url]
shortname 为本地的版本库,例如:
# 提交到 Github
$ git remote add origin git@github.com:tianqixin/runoob-git-test.git
# 把本地库的所有内容推送到远程库上
$ git push -u origin master
说明:由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
git remote rm name # 删除远程仓库
git remote rename old_name new_name # 修改仓库名
git pull <远程主机名> <远程分支名>:<本地分支名>
$ git pull
$ git pull origin
将远程主机 origin 的 master 分支拉取过来,与本地的 brantest 分支合并。
$ git pull origin master:brantest
如果远程分支是与当前分支合并,则冒号后面的部分可以省略。
$ git pull origin master
git push <远程主机名> <本地分支名>:<远程分支名>
如果本地分支名与远程分支名相同,则可以省略冒号:
git push <远程主机名> <本地分支名>
以下命令将本地的 master 分支推送到 origin 主机的 master 分支。
$ git push origin master
相等于:
$ git push origin master:master
要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git
;
关联一个远程库时必须给远程库指定一个名字,origin
是默认习惯命名;
关联后,使用命令git push -u origin master
第一次推送master分支的所有内容;
此后,每次本地提交后,只要有必要,就可以使用命令git push origin master
推送最新修改;
分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!