Git是一种分布式版本控制系统(distriubted version control system),它能够追踪每个文件的历史记录,能告诉你在每个时间节点上的文件保存状态。每一次改变就相当于做了一次commit。

1、创建一个新目录

$ mkdir example
$ cd example
$ git init
Initialized empty Git repository in /Users/yiguan/Desktop/example/.git/

通过git init命令将该路径标记为Git路径。这个时候你可以通过git statuts查看该Git路径的状态

$ git status
On branch master No commits yet nothing to commit (create/copy files and use "git add" to track)

它告诉你了当前你在哪一个分支上(master),没有任何commit.

2、添加文件
在Git路径下创建并保存一个Python文件,如下hello.py

# hello.py
print('Hello Git')

这个时候再查看status,会发现:

$ git status
On branch master No commits yet 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告诉你在该路径下发现了一个新文件,但是该文件没有被追踪,也就是这个文件仅仅是一个普通文件,不属于Git系统。那么怎样告诉Git这个文件是需要追踪的呢?

$ git add hello.py
$ git status
On branch master No commits yet Changes to be committed:  (use "git rm --cached <file>..." to unstage)    new file:   hello.py

通过git add * 可以将hello.py文件添加到Git系统中。

3、commit(声明改变)
当你对文件作出修改的时候,你要告诉Git做一个快照,可以通过git commit来实现:

$ git commit -m "create hello.py"
[master (root-commit) 158d745] create hello.py 1 file changed, 1 insertion(+) create mode 100644 hello.py

$ git statusOn branch master nothing to commit, working tree clean

命令选择 -m 是你自己用来定义,提示哪些改变。
这儿创建了一个hello.py的文件,git statuts提示“clean”,也就意味着所有的修改都被Git所记录了。

4、.gitignore
status命令使用起来很方便,会经常用到。但是有时候你会看到很多你不希望被追踪的文件被标记为”untrack”,你希望Git能够忽略掉这些文件。这个时候就可以用.gitignore
比如,我们在当前路径下又创建了一个新的Python文件,myname.py

# myname.py
def get_name():
    return "WANG"

这个时候我们修改hello.py,在其中调用myname.py

# hello.py
import myname
name = myname.get_name()
print('hello, {}'.format(name))

注意在python3中,当我们把一个python文件作为模块导入另一个文件中时,python会自动创建一个__pycache__的文件:

$ ls
hello.py    myname.py

$ python3 hello.py
hello, WANG

$ ls
__pycache__    hello.py    myname.py

这是时候查看status可以看到一个文件做了修改,另外两个文件没有被追踪(“untrack”)。

$ 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 Untracked files:  (use "git add <file>..." to include in what will be committed)    __pycache__/    myname.py no changes added to commit (use "git add" and/or "git commit -a")

下面我们先整理一下文件,将myname.py和hello.py加入到Git中,并做快照(commit)。

$ git add myname.py hello.py 

$ git commit -m "added myname module"
[master 5b48c43] added myname module 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 myname.py
$ git status
On branch master Untracked files:  (use "git add <file>..." to include in what will be committed)    __pycache__/ nothing added to commit but untracked files present (use "git add" to track)

因为__pycache__是python在导入模块的时候自动生成的,我们不想在Git中对它进行追踪,所以需要创建一个.gitignore文件,并把它加入到.gitignore文件中。方法很简单,只需要在.gitignore文件中写入该文件(夹)的名字即可。
注意.gitignore前面有一个点,不可以省略。

# .gitignore
__pycache__

这个时候查看status,可以发现__pycache__已经不在被提示了。

$ git status
On branch master Untracked files:  (use "git add <file>..." to include in what will be committed)    .gitignore nothing added to commit but untracked files present (use "git add" to track)

当然,这个时候.gitignore还是一个普通文件,需要将它加入到Git中,并做快照。

$ git add .gitignore
$ git commit -m "created .gitignore
"
[master 799791d] created .gitignore 1 file changed, 1 insertion(+) create mode 100644 .gitignore

这个时候就完美了:

$ git status
On branch master nothing to commit, working tree clean

这儿会有一个问题,为什么不把所有的文件都放入Git中追踪呢?为什么还要选择性忽略掉一些文件(比如__pycache__)?
记住一点:
只把源文件放入版本控制中, 不要放自动生成的文件
所谓源文件就是你自己编写的,自动生成文件是电脑生成的,通常是在执行源文件的过程中产生的。首先,把自动生成的文件加入到版本控制会占用很多资源,每次生成一个不同的文件,都要追踪,很复杂,而且同意造成文件之间的冲突;其次,自动生成的文件通常比较大,加入到版本控制中会使使用者下载该文件,即使他不一定用到这些文件。
所以对于二进制文件和大文件的版本控制一定要小心。这版本控制中,Git通常不会把整个文件进行快照备份,只是通过算法,将其中的修改的部分做标记,这对避免了很多大文件的占用空间,但是对于二进制文件,比如pg格式和mp3格式文件,通常难以确定修改的部分,所以会将整个文件都进行快照备份。
另外,还有一些含有个人信息或者不想公开的文件,也应该在.gitignore中。

5、git log
git log是另一个比较常用的命令,它能够显示你每一次commit的历史记录:

$ git log
commit 799791d244c618a5dc7e2096709dc9ce3a8e4e38 (HEAD -> master) Author: Yiguan <wang.yiguan@......com> Date:   Fri Jul 20 11:59:15 2018 +1000    created .gitignore commit 5b48c43cf949fb8c3a21cd5013e3cfd13dd2565c Author: Yiguan <wang.yiguan@.......com> Date:   Fri Jul 20 11:47:38 2018 +1000    added myname module commit 158d745e86229acb130b1bc15343c78a38dbc93d Author: Yiguan <wang.yiguan@.......com> Date:   Fri Jul 20 11:16:35 2018 +1000    create hello.py

每一个commit之后都有一个长串字符,这个是所有文件的hash值,使用SHA的加密方法。

另外可以通过--graph选项,以简图的形式显示出git的树状结构,很直观形象。


6、查看历史版本
我们可以通过git checkout来查看历史版本,比如我们要查看第二次commit时候的文件。

$ git checkout 5b48c43cf949fb8c3a21cd5013e3cfd13dd2565c

Note: checking out '5b48c43cf949fb8c3a21cd5013e3cfd13dd2565c'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example:  git checkout -b <new-branch-name> HEAD is now at 5b48c43... added myname module

可以看到很多信息。这个时候查看status,你会发现.gitignore已经不在了:

$ git status
HEAD detached at 5b48c43 Untracked files:  (use "git add <file>..." to include in what will be committed)    __pycache__/ nothing added to commit but untracked files present (use "git add" to track)

那么如何再回到最后的那个版本呢,如果我们知道SHA值,可以直接checkout,回到最后的版本:

$ git checkout 799791d244c618a5dc7e2096709dc9ce3a8e4e38
Previous HEAD position was 5b48c43... added myname module HEAD is now at 799791d... created .gitignore

不过最常使用的是通过git checkout master命令来回到最后的一个版本。

7、创建分支
如果你和你的同事一起在创建一个代码工程,在你未完成对代码的调试之前,不会进行commit,但是在master上进行你的代码调试是很为不安全的。这个时候你就需要创建一个分支。

$ git status
On branch master nothing to commit, working tree clean

$ git checkout -b my_new_feature
Switched to a new branch 'my_new_feature'

$ git status
On branch my_new_feature nothing to commit, working tree clean

-b选择项用来说明创建分支,创建完之后可以看到Git告诉你你现在在my_new_feature的分支上。
我们现在对hello.py进行修改:

# hello.py
import myname
name = myname.get_name()
print('hello, {}'.format(name))
print("END") #添加此行

然后将此修改添加到Git中。

$ git add hello.py
$ git commit -m "added code END"
[my_new_feature 0479887] added code END 1 file changed, 1 insertion(+)

这个时候查看log,可以看到:

$ git log
commit 0479887a450fb4e2baa041ca8fcabcbc4f260c73 (HEAD -> my_new_feature) Author: Yiguan <wang.yiguan@163.com> Date:   Fri Jul 20 12:48:40 2018 +1000    added code END commit 799791d244c618a5dc7e2096709dc9ce3a8e4e38 (master) Author: Yiguan <wang.yiguan@163.com> Date:   Fri Jul 20 11:59:15 2018 +1000    created .gitignore (...略...)

我们已经在my_new_feature分支上完成了对hello.py的修改。

这个时候回到主支(master):

$ git  checkout master
Switched to branch 'master'
$ git log
commit 799791d244c618a5dc7e2096709dc9ce3a8e4e38 (HEAD -> master) Author: Yiguan <wang.yiguan@163.com> Date:   Fri Jul 20 11:59:15 2018 +1000    created .gitignore commit 5b48c43cf949fb8c3a21cd5013e3cfd13dd2565c Author: Yiguan <wang.yiguan@163.com> Date:   Fri Jul 20 11:47:38 2018 +1000    added myname module commit 158d745e86229acb130b1bc15343c78a38dbc93d Author: Yiguan <wang.yiguan@163.com> Date:   Fri Jul 20 11:16:35 2018 +1000    create hello.py

在主支上并没有出现你对hello.py的修改!如果要比较分支(my_new_feature)和主支(master)的区别可以:

$ git show-branch my_new_feature master
! [my_new_feature] added code END * [master] created .gitignore -- +  [my_new_feature] added code END +* [master] created .gitignore

8、合并
之前创建了my_new_feature的分支,讲过调试,认为代码没有问题。现在我们想把它合并到主支master上。
先检查一下比较各个分支,这次我们使用—sha1-name选项加上了SHA

$ git show-branch --sha1-name my_new_feature master
! [my_new_feature] added code END * [master] created .gitignore -- +  [0479887] added code END +* [799791d] created .gitignore

这是仅仅需要git merge命令就可以将my_new_feature合并到master中:

$ git checkout master
Already on 'master'

$ git merge my_new_feature
Updating 799791d..0479887 Fast-forward hello.py | 1 + 1 file changed, 1 insertion(+)

除了merge之外,rebase也可以用来合并分支。

9、撤销创建
有两个常用命令, 一个是git reset,另一个是git revert.
reset相当于抹掉了分支,如果你将reset之后的Git上传,别人讲看不到你抹掉的内容;而revert相当于你重新创建了一个之前的版本,别人能够看到你的修改。

10、远程创建
上面所说的内容都是在本地完成的操作,如果我们想把我们的工程共享给其他人,或者发布,那么就需要远程Git.
常用到的远程Git命令有:

clone
fetch
pull
push

(1) clone
如果你知道一个Git的远程地址,那么可以直接使用clone将其拷贝到本地。例如

git clone git@github.com:Yiguan/CheckOverlap.git

这就意味着你将在本地建立一个和远程一模一样的Git,包括所有的commit。

(2) fetch和pull相似,根据远程Git更新本地Git,只是fetch不对分支做合并,pull会合并相同的分支。
(3) push 将本地Git合并到远程Git.

总结,一个Git完整的流程为:
. git status - 查看状态,确保当前内容是”clean”
. git pull - 从远程获取最新的版本
. 编辑修改文件,测试修改的代码
. git status - 核实修改的文件,注意一些没有被track的文件
. git add [file] - 将修改的文件添加到git中
. git commit -m “message” - 快照,添加commit
. git push origin [branch-name] - 将修改推送到远程

最后一个很有意思很形象的学习git的网站:
https://learngitbranching.js.org/


========== THE END ==========

参考资料:https://realpython.com/python-git-github-intro/

Git入门及常用命令_java