Git简易使用指南

Git是目前最流行的版本控制系统,这里以GitHub为例,介绍git的基本使用。

Git简介

如果在看这篇文章之前,你习惯把自己的代码保存在本地的话,那你就应该改变一下了。如果由于某些突发情况丢失在本地的数据,那时候如果你突然想起来有很多很重要的代码还没有备份,那么此时你的内心一定是崩溃的,使用GitHub,我们可以很方便的把代码上传到远程服务器,即使有一天你突然在另一个地方想用到这些代码,也不用担心,只要你能上网,几条命令就可以把代码下载下来了。
还有一些时候,你在改完一个功能的时候,突然想起来也许之前版本的还需要,但是你已经很手欠的把他们删掉了,没关系,在git的世界里,你是有后悔药可以吃的,可以很方便的回退到之前版本。
团队合作也是很重要的事情,但是在合作的过程中可能遇到各种各样的问题,这些在git上都得到了有效的解决。
git还有很多流弊的功能,在使用过程中挖掘吧。

如何使用Git

在Linux系统下(这里以Ubuntu为例),你可以直接安装git。
sudo apt install git
在Windows系统下,可以使用Git for Windows,而我通常用Cygwin,它是一个在Windows上运行的类UNIX环境,官网是:https://www.cygwin.com/

现在你已经能打开一个终端,并且成功的运行了git命令。运行git命令后,可以看到如下界面:

netcon@conwlt ~
$ git
usage: git [--version] [--help] [-C <path>] [-c name=value]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>]

These are common Git commands used in various situations:

start a working area (see also: git help tutorial)
   clone      Clone a repository into a new directory
   init       Create an empty Git repository or reinitialize an existing one

work on the current change (see also: git help everyday)
   add        Add file contents to the index
   mv         Move or rename a file, a directory, or a symlink
   reset      Reset current HEAD to the specified state
   rm         Remove files from the working tree and from the index

examine the history and state (see also: git help revisions)
   bisect     Use binary search to find the commit that introduced a bug
   grep       Print lines matching a pattern
   log        Show commit logs
   show       Show various types of objects
   status     Show the working tree status

grow, mark and tweak your common history
   branch     List, create, or delete branches
   checkout   Switch branches or restore working tree files
   commit     Record changes to the repository
   diff       Show changes between commits, commit and working tree, etc
   merge      Join two or more development histories together
   rebase     Reapply commits on top of another base tip
   tag        Create, list, delete or verify a tag object signed with GPG

collaborate (see also: git help workflows)
   fetch      Download objects and refs from another repository
   pull       Fetch from and integrate with another repository or a local branch
   push       Update remote refs along with associated objects

'git help -a' and 'git help -g' list available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.

看到上述信息说明git安装成功,这时候我们要设置一下自己的信息。
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
例如我本人就是:

netcon@conwlt ~
$ git config --global user.email "netcon@live.com"

netcon@conwlt ~
$ git config --global user.name "netcon"

项目仓库(Repository)

我们首先创建一个文件夹hello-git,并进入到该文件夹下:

netcon@conwlt ~
$ mkdir hello-git

netcon@conwlt ~
$ cd hello-git/

接下来我们初始化一个仓库,使用git init命令即可:

netcon@conwlt ~/hello-git
$ git init
Initialized empty Git repository in /home/netcon/hello-git/.git/

netcon@conwlt ~/hello-git
$ ls -a
./  ../  .git/

可以看到运行git init后,在当前目录创建了一个.git文件夹,这个就是git用来管理版本库的,平时我们没事不要动它。
之后我们就在这个目录下写代码了,就像我们以前用IDE创建一个新工程一样。而这个目录就是一个版本库。

基本操作

首先我们来创建一个新文件hello.py,里面的内容只有一行print('Hello Git!')。这里推荐使用VIM,对其不太熟悉的也可以使用其他工具。在Linux下可以使用gedit,而在Windows下推荐使用Nodepad++,不建议使用记事本。编辑完成后可是使用git status命令查看当前状态。

netcon@conwlt ~/hello-git
$ vim hello.py

netcon@conwlt ~/hello-git
$ git status
On branch master

Initial commit

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

        hello.py

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

根据提示我们可以看到,git很聪明地发现我们编辑了hello.py文件,并且说它Untracked,要我们git add <file>。那么我们就按它说的做,然后再看一下状态:(其他的提示信息你可能觉得迷惑,我们先不管它们)

netcon@conwlt ~/hello-git
$ git add hello.py

netcon@conwlt ~/hello-git
$ git status
On branch master

Initial commit

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

        new file:   hello.py

这次hello.py的状态已经是add之后的了,git提示我们可以commit它,也可以使用git rm --cached <file>使它返回还未add的状态。我们接下来commit它。

netcon@conwlt ~/hello-git
$ git commit -m 'create hello.py'
[master (root-commit) eb3a0b6] create hello.py
 1 file changed, 1 insertion(+)
 create mode 100644 hello.py

git commit -m <commit message>命令中我们使用了-m参数,这个是快速添加commit message,不用这个参数的话会到一个文本编辑页面添加它,它是我们每次提交的介绍,方便以后查看以前的版本信息,和注释差不多。

那我们修改文件后会怎么样呢?我们把hello.py中的内容改成print('Hello conw.net!'),然后再查看状态:

netcon@conwlt ~/hello-git
$ vim hello.py

netcon@conwlt ~/hello-git
$ 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:   hello.py

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

git又很聪明地发现我们修改了文件,那我们能不能知道改了哪些内容呢?可以,使用git diff命令即可:

netcon@conwlt ~/hello-git
$ git diff
diff --git a/hello.py b/hello.py
index 4f013ae..7ba0fef 100644
--- a/hello.py
+++ b/hello.py
@@ -1 +1 @@
-print('Hello Git!')
+print('Hello conw.net')

果然是很强大,需要注意的是这种提示仅限于这种文本文件,如果是二进制文件比如一个图片的话,那么git只是知道它修改了,但是不会提示它改了哪里的。接下来我们使用git add hello.pygit commit -m 'modify hello.py'提交修改。

netcon@conwlt ~/hello-git
$ git add .

netcon@conwlt ~/hello-git
$ git commit -m 'modify hello.py'
[dev d441413] modify hello.py
 1 file changed, 1 insertion(+), 1 deletion(-)

现在我们用到了5条git命令,用它们就可以简单地使用git了:
git init:初始化版本库
git status:查看版本库状态
git add <file>:把文件添加到暂存区(index)。
git commit:把暂存区中的文件提交到分支(branch)。
git diff:查看文件具体修改了哪里。

看了上面的几条命令,如果你觉得迷茫的话呢,那么就先来了解一些基本概念吧。

工作区与版本库

工作区就是我们编写代码所在的目录,我们都是直接操作这里面的内容的。
那么版本库是干什么的呢?简单来说,我们每次commit都生成了一个新的版本,Git可以管理我们之前各个版本的代码,这些操作都是依赖版本库的。我们先执行一下git log查看之前的版本:

netcon@conwlt ~/hello-git
$ git log
commit d441413cb8f76ed4a4cbea6cd3d2ee9342bc6156
Author: netcon <netcon@live.com>
Date:   Sat Sep 24 00:57:41 2016 +0800

    modify hello.py

commit eb3a0b68304f827f214f6fb567fafa8626f4f555
Author: netcon <netcon@live.com>
Date:   Sat Sep 24 00:33:07 2016 +0800

    create hello.py

netcon@conwlt ~/hello-git
$

可以很清晰地看到了我们这个项目之前的版本,看到之前的commit message,我们也想起来每个版本的细节了。
版本库中有暂存区(index)的概念,每次我们在工作区编辑好代码后,运行git add <file>命令,就是把修改后的文件提交到暂存区,我们可以多次修改文件、多次git add <file>,最后运行git commit统一把所有暂存区中的文件提交到分支,也就是创建了一个新版本,我们在git log能够看到。
我们提到了分支(branch),那它是什么意思呢?我们通过git log命令看到了项目之前的版本,就像一条时间线把这个项目的各个阶段连接起来,每次commit就是时间线上的一个节点,分支就是指向时间线中某个节点的指针。
一个项目仓库可以有多个分支,通常我们初始化仓库后默认创建的分支叫master,我们可以通过git branch <branch name>创建新分支,并且通过git checkout <branch name>切换到指定分支。

netcon@conwlt ~/hello-git
$ git status
On branch master
nothing to commit, working directory clean

netcon@conwlt ~/hello-git
$ git branch dev

netcon@conwlt ~/hello-git
$ git checkout dev
Switched to branch 'dev'

netcon@conwlt ~/hello-git
$ git status
On branch dev
nothing to commit, working directory clean

可以看到,我们最早是在master分支,然后创建并切换到了dev分支,我们目前的状态是这样的:
1.png
接下来我们在dev分支上做一些修改并commit:

netcon@conwlt ~/hello-git
$ vim hello.py

netcon@conwlt ~/hello-git
$ cat hello.py
print('Hello conw.net')
print('This is dev branch')

netcon@conwlt ~/hello-git
$ git add hello.py

netcon@conwlt ~/hello-git
$ git commit -m 'add dev mark'
[dev 4edf04c] add dev mark
 1 file changed, 1 insertion(+)

现在的状态应该是这样的:
2.png
接下来我们切换到master分支,查看hello.py的内容,发现在dev分支进行的修改在这里果然不生效:

netcon@conwlt ~/hello-git
$ git checkout master
Switched to branch 'master'

netcon@conwlt ~/hello-git
$ cat hello.py
print('Hello conw.net')

netcon@conwlt ~/hello-git
$

我们尝试修改master分支并提交它:

netcon@conwlt ~/hello-git
$ vim hello.py

netcon@conwlt ~/hello-git
$ cat hello.py
print('Hello conw.net')
print('This is master branch')

netcon@conwlt ~/hello-git
$ git add hello.py

netcon@conwlt ~/hello-git
$ git commit -m 'add master mark'
[master fbd0bf5] add master mark
 1 file changed, 1 insertion(+)

此时的状态就应该是这样的:
3.png

此时就能看出为什么要使用分支这个词了,也就是说,所谓的时间线,在逻辑上,并不是一条直线,而是一个树形结构的。
在上面例子中,不同分支上的代码是不一样的:

netcon@conwlt ~/hello-git
$ git status
On branch master
nothing to commit, working directory clean

netcon@conwlt ~/hello-git
$ cat hello.py
print('Hello conw.net')
print('This is master branch')

netcon@conwlt ~/hello-git
$ git checkout dev
Switched to branch 'dev'

netcon@conwlt ~/hello-git
$ git status
On branch dev
nothing to commit, working directory clean

netcon@conwlt ~/hello-git
$ cat hello.py
print('Hello conw.net')
print('This is dev branch')

netcon@conwlt ~/hello-git
$

你还可以使用git merge命令把在两个分支上的工作合并到一起。
理解了工作区、版本库、暂存区、分支的概念,我们可以学习更多的git命令了。

版本回退

我们已经知道了git能保存我们之前每次commit后的代码,并且通过git log命令可以查看之前代码的每个版本,那么我们如何回到之前的某个版本呢?使用git reset --hard <commit id>即可。

netcon@conwlt ~/hello-git
$ git status
On branch dev
nothing to commit, working directory clean

netcon@conwlt ~/hello-git
$ git log
commit 4edf04cd1223abf07e36ae5c2d878c243395ecc9
Author: netcon <netcon@live.com>
Date:   Sat Sep 24 01:44:08 2016 +0800

    add dev mark

commit d441413cb8f76ed4a4cbea6cd3d2ee9342bc6156
Author: netcon <netcon@live.com>
Date:   Sat Sep 24 00:57:41 2016 +0800

    modify hello.py

commit eb3a0b68304f827f214f6fb567fafa8626f4f555
Author: netcon <netcon@live.com>
Date:   Sat Sep 24 00:33:07 2016 +0800

    create hello.py

netcon@conwlt ~/hello-git
$ git reset --hard d441
HEAD is now at d441413 modify hello.py

netcon@conwlt ~/hello-git
$ cat hello.py
print('Hello conw.net')

netcon@conwlt ~/hello-git
$

注意这里的commit id我只输入了前几位,而不用全部输入,只要能标识唯一版本就可以,git会自动找到符合条件的分支。
这样我们就回到的之前的版本了,那么我们如何回到未来的版本(也就是add dev mark那个版本)呢?方法之一是找到未来版本的commit id,但是如果我们不小心已经把终端关掉找不到之前的commit了呢?还有另一个办法就是git reflog

netcon@conwlt ~/hello-git
$ git reflog
d441413 HEAD@{0}: reset: moving to d441
4edf04c HEAD@{1}: checkout: moving from master to dev
fbd0bf5 HEAD@{2}: commit: add master mark
d441413 HEAD@{3}: checkout: moving from dev to master
4edf04c HEAD@{4}: commit: add dev mark
d441413 HEAD@{5}: checkout: moving from master to dev
d441413 HEAD@{6}: commit: modify hello.py
eb3a0b6 HEAD@{7}: commit (initial): create hello.py

netcon@conwlt ~/hello-git
$ git reset --hard 4edf
HEAD is now at 4edf04c add dev mark

netcon@conwlt ~/hello-git
$ cat hello.py
print('Hello conw.net')
print('This is dev branch')

netcon@conwlt ~/hello-git
$

我们在reflog的记录中找到了未来版本的commit id,我们又安全的回来了!
小技巧:我们可以使用git reset --hard HEAD^快速回到之前的版本,这个HEAD表示当前版本,HEAD^表示前一个版本,HEAD^^表示前两个版本~

GitHub

现在我们已经可以基本的使用git来管理我们的代码了,git是一个很先进的版本控制系统,相比于传统的SVN等,它又快又好用(快了不止一点,好用了不止一点)。目前还有很多免费的代码托管网站,可以用作我们的远程代码仓库。这里介绍的就是其中最牛逼的GitHub(https://github.com/)。
GitHub在中国有时可能访问异常,尽量使用https访问。
首先就是注册登录,无需多说。
登录后,点击右上角的头像选择Your profile,进入你的github主页。
我们先创建一个项目仓库(repository),创建完成后进入这个页面。
4.png
注意图中红框部分,我们选择HTTPS类型的地址,原因后面会说。
接下来我们把本地的代码push到github上。
我们回到本地仓库,把代码push到远程仓库前,要保证当前仓库有一个README.md文件,它是仓库的说明文件,我们先简单创建它:

netcon@conwlt ~/hello-git
$ echo "# hello-git" >> README.md

netcon@conwlt ~/hello-git
$ git add README.md

netcon@conwlt ~/hello-git
$ git commit -m 'create README.md'
[dev c1f1871] create README.md
 1 file changed, 1 insertion(+), 1 deletion(-)

然后用git remote add <name> <url>设置一下远程仓库:

netcon@conwlt ~/hello-git
$ git remote add origin https://github.com/conwnet/hello-git.git

这里的name是origin,github上默认是这个名字,也可以改成其它的。然后我们用git push origin master即可:

netcon@conwlt ~/hello-git
$ git push origin master
Username for 'https://github.com': conwnet
Password for 'https://conwnet@github.com':
Counting objects: 9, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (9/9), 705 bytes | 0 bytes/s, done.
Total 9 (delta 0), reused 0 (delta 0)
To https://github.com/conwnet/hello-git.git
 * [new branch]      master -> master

我们成功把master分支push到了远程仓库。输入密码那里不可见,不过放心你的确是成功输入进去了。回到github选择刚才创建的项目,可以看到我们刚才编辑的两个文件:
5.png
由于代码可能多人合作,也可能我们在其他计算机上修改过。所以当前在本地的代码可能和在github的不一致,这样的话我们是无法直接push上去的,此时我们可以使用git pull origin master命令把远程代码和本地代码合并,如果发生冲突也会有相应提示,我们处理好冲突后就可以再push上去了。
另一个常用的操作是克隆(clone),我们可以使用git clone <url>命令把一个远程仓库复制到本地。
如果你看到一个别人的项目觉得非常棒,你可以使用Fork操作,把这个项目Fork到自己的仓库中,就可以对其继续进行开发了。当你修改之后还可以向原作者发起pull request,就是把你的修改合并到原项目,这样的话项目就会在大家的共同努力下不断发展壮大。
关于远程仓库的两种地址问题:HTTPS地址是形如https://github.com/conwnet/hello-git.git这样的地址,它的特点是可以用帐号密码push和pull。而形如git@github.com:conwnet/hello-git.git的地址称为SSH地址,要使用这样的地址我们需要在Account Settings里面添加相应的SSH公钥,添加成功后不用每次验证密码,很方便,关于这方面的知识是可以自己去网上搜索,这里不再赘述。

Git常用操作总结

git clone <url>:把一个远程仓库克隆到一个新文件夹
git init:创建一个新的本地仓库
git add <file>:把文件添加到暂存区(index)。
git mv <file1> <file2>:移动或者重命名文件
git rm <file>:删除暂存区和工作区的文件。
git status:显示当前分支的状态信息
git commit -m <commit message>:把暂存区的文件提交到分支
git branch <branch name>:创建新的分支
git checkout <branch name>:切换到指定分支
git merge <branch name>:把指定分支合并到当前分支
git diff <file>:显示文件修改了哪里
git log:显示当前分支的各版本信息
git reflog:显示当前分支的变动日志
git reset --hard <commit id>:回退(或者前进)到指定的版本
git remote add <name> <url>:添加一个远程库
git push <name> <branch name>:把本地的指定分支同步到远程仓库
git pull <name> <branch name>:把远程仓库的指定分支同步到本地

标签: git

仅有一条评论

  1. 0xcafebabe 0xcafebabe

    looks good

添加新评论