首页 > 代码库 > Coding.net进阶,使用Git管理代码
Coding.net进阶,使用Git管理代码
原文来自:http://conw.net/archives/18/
(不是抄袭,那是我自己的博客,原文查看更清晰)
Git是目前最流行的版本控制系统,这里以GitHub为例,介绍git的基本使用。
Git简介
如果在看这篇文章之前,你习惯把自己的代码保存在本地的话,那你就应该改变一下了。可能由于某些情况,我们会失去在本地保存的数据,那时候如果你突然想起来有很多很重要的代码还没有备份,那么此时你的内心一定是崩溃的,有了github,我们可以很方便的把代码上传到远程服务器,即使有一天你突然在另一个地方想用到这些代码,也不用担心,只要你能上网,一条命令就可以把代码下载下来了。
还有一些时候,你在改完一个功能的时候,突然想起来也许之前版本的还需要,但是你已经很手欠的把他们删掉了,没关系,在git的世界里,你是有后悔药可以吃的,可以很方便的回退到之前版本。
团队合作也是很重要的事情,但是在合作的过程中可能遇到各种各样的问题,这些在git上都得到了有效的解决。
git还有很多流弊的功能,在使用过程中挖掘吧。
如何使用Git
在Linux系统下(这里以Ubuntu为例),你可以直接安装git。sudo apt install git
在Windows系统下,我通常用Cygwin,这里提供一个下载链接,包含了一些我个人的常用配置,压缩包里有使用说明,请先阅读一下。
链接: http://pan.baidu.com/s/1eRQzbEi
密码: 689t
现在你已经能打开一个终端,并且成功的运行git命令了。运行git命令后,可以看到如下界面:
netcon@conwlt ~$ gitusage: 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 onework 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 indexexamine 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 statusgrow, 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 GPGcollaborate (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 someconcept 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-gitnetcon@conwlt ~$ cd hello-git/
接下来我们初始化一个仓库,使用git init命令即可:
netcon@conwlt ~/hello-git$ git initInitialized 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.pynetcon@conwlt ~/hello-git$ git statusOn branch masterInitial commitUntracked files: (use "git add <file>..." to include in what will be committed) hello.pynothing 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.pynetcon@conwlt ~/hello-git$ git statusOn branch masterInitial commitChanges 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$ git statusOn branch masterChanges 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.pyno changes added to commit (use "git add" and/or "git commit -a")
git又很聪明地发现我们修改了文件,那我们能不能知道改了哪些内容呢?可以,使用git diff
命令即可:
netcon@conwlt ~/hello-git$ git diffdiff --git a/hello.py b/hello.pyindex 4f013ae..7ba0fef 100644--- a/hello.py+++ b/hello.py@@ -1 +1 @@-print(‘Hello Git!‘)+print(‘Hello conw.net‘)
果然是很强大,需要注意的是这种提示仅限于这种文本文件,如果是二进制文件比如一个图片的话,那么git只是知道它修改了,但是不会提示它改了哪里的。接下来我们使用git add hello.py
和git 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 logcommit d441413cb8f76ed4a4cbea6cd3d2ee9342bc6156Author: netcon <netcon@live.com>Date: Sat Sep 24 00:57:41 2016 +0800 modify hello.pycommit eb3a0b68304f827f214f6fb567fafa8626f4f555Author: netcon <netcon@live.com>Date: Sat Sep 24 00:33:07 2016 +0800 create hello.pynetcon@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 ‘切换到指定分支。
netcon@conwlt ~/hello-git$ git statusOn branch masternothing to commit, working directory cleannetcon@conwlt ~/hello-git$ git branch devnetcon@conwlt ~/hello-git$ git checkout devSwitched to branch ‘dev‘netcon@conwlt ~/hello-git$ git statusOn branch devnothing to commit, working directory clean
可以看到,我们最早是在master分支,然后创建并切换到了dev分支,我们目前的状态是这样的:
接下来我们在dev分支上做一些修改并commit:
netcon@conwlt ~/hello-git$ vim hello.pynetcon@conwlt ~/hello-git$ cat hello.pyprint(‘Hello conw.net‘)print(‘This is dev branch‘)netcon@conwlt ~/hello-git$ git add hello.pynetcon@conwlt ~/hello-git$ git commit -m ‘add dev mark‘[dev 4edf04c] add dev mark 1 file changed, 1 insertion(+)
现在的状态应该是这样的:
接下来我们切换到master分支,查看hello.py
的内容,发现在dev分支进行的修改在这里果然不生效:
netcon@conwlt ~/hello-git$ git checkout masterSwitched to branch ‘master‘netcon@conwlt ~/hello-git$ cat hello.pyprint(‘Hello conw.net‘)netcon@conwlt ~/hello-git$
我们尝试修改master分支并提交它:
netcon@conwlt ~/hello-git$ vim hello.pynetcon@conwlt ~/hello-git$ cat hello.pyprint(‘Hello conw.net‘)print(‘This is master branch‘)netcon@conwlt ~/hello-git$ git add hello.pynetcon@conwlt ~/hello-git$ git commit -m ‘add master mark‘[master fbd0bf5] add master mark 1 file changed, 1 insertion(+)
此时的状态就应该是这样的:
此时就能看出为什么要使用分支这个词了,也就是说,所谓的时间线,在逻辑上,并不是一条直线,而是一个树形结构的。
在上面例子中,不同分支上的代码是不一样的:
netcon@conwlt ~/hello-git$ git statusOn branch masternothing to commit, working directory cleannetcon@conwlt ~/hello-git$ cat hello.pyprint(‘Hello conw.net‘)print(‘This is master branch‘)netcon@conwlt ~/hello-git$ git checkout devSwitched to branch ‘dev‘netcon@conwlt ~/hello-git$ git statusOn branch devnothing to commit, working directory cleannetcon@conwlt ~/hello-git$ cat hello.pyprint(‘Hello conw.net‘)print(‘This is dev branch‘)netcon@conwlt ~/hello-git$
理解了工作区、版本库、暂存区、分支的概念,我们可以学习更多的git命令了。
版本回退
我们已经知道了git能保存我们之前每次commit后的代码,并且通过git log
命令可以查看之前代码的每个版本,那么我们如何回到之前的某个版本呢?使用git reset --hard <commit id>
即可。
netcon@conwlt ~/hello-git$ git statusOn branch devnothing to commit, working directory cleannetcon@conwlt ~/hello-git$ git logcommit 4edf04cd1223abf07e36ae5c2d878c243395ecc9Author: netcon <netcon@live.com>Date: Sat Sep 24 01:44:08 2016 +0800 add dev markcommit d441413cb8f76ed4a4cbea6cd3d2ee9342bc6156Author: netcon <netcon@live.com>Date: Sat Sep 24 00:57:41 2016 +0800 modify hello.pycommit eb3a0b68304f827f214f6fb567fafa8626f4f555Author: netcon <netcon@live.com>Date: Sat Sep 24 00:33:07 2016 +0800 create hello.pynetcon@conwlt ~/hello-git$ git reset --hard d441HEAD is now at d441413 modify hello.pynetcon@conwlt ~/hello-git$ cat hello.pyprint(‘Hello conw.net‘)netcon@conwlt ~/hello-git$
注意这里的commit id我只输入了前几位,而不用全部输入,只要能标识唯一版本就可以,git会自动找到符合条件的分支。
这样我们就回到的之前的版本了,那么我们如何回到未来的版本(也就是add dev mark
那个版本)呢?方法之一是找到未来版本的commit id,但是如果我们不小心已经把终端关掉找不到之前的commit了呢?还有另一个办法就是git reflog
。
netcon@conwlt ~/hello-git$ git reflogd441413 HEAD@{0}: reset: moving to d4414edf04c HEAD@{1}: checkout: moving from master to devfbd0bf5 HEAD@{2}: commit: add master markd441413 HEAD@{3}: checkout: moving from dev to master4edf04c HEAD@{4}: commit: add dev markd441413 HEAD@{5}: checkout: moving from master to devd441413 HEAD@{6}: commit: modify hello.pyeb3a0b6 HEAD@{7}: commit (initial): create hello.pynetcon@conwlt ~/hello-git$ git reset --hard 4edfHEAD is now at 4edf04c add dev marknetcon@conwlt ~/hello-git$ cat hello.pyprint(‘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),创建完成后进入这个页面。
注意图中红框部分,我们选择HTTPS类型的地址,原因后面会说。
接下来我们把本地的代码push到github上。
我们回到本地仓库,把代码push到远程仓库前,要保证当前仓库有一个README.md
文件,它是仓库的说明文件,我们先简单创建它:
netcon@conwlt ~/hello-git$ echo "# hello-git" >> README.mdnetcon@conwlt ~/hello-git$ git add README.mdnetcon@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 masterUsername for ‘https://github.com‘: conwnetPassword 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选择刚才创建的项目,可以看到我们刚才编辑的两个文件:
由于代码可能多人合作,也可能我们在其他计算机上修改过。所以当前在本地的代码可能和在github的不一致,这样的话我们是无法直接push上去的,此时我们可以使用git pull origin master
命令把远程代码和本地代码合并,如果发生冲突也会有相应提示,我们处理好冲突后就可以再push上去了。
关于远程仓库的两种地址问题:HTTPS地址是形如https://github.com/conwnet/hello-git.git
这样的地址,它的特点是可以用帐号密码push和pull。而形如git@github.com:conwnet/hello-git.git
的地址称为SSH地址,要使用这样的地址我们需要在Account Settings里面添加相应的SSH公钥,添加成功后不用每次验证密码,很方便,关于这方面的知识是可以自己去网上搜索,这里不再赘述。
另一个常用的操作是克隆(clone),我们可以使用git clone <url>
命令把一个远程仓库复制到本地。
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 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>
:把远程仓库的指定分支同步到本地
Coding.net进阶,使用Git管理代码