版本控制工具——Git常用操作(上)

本文由雲+社區發表

作者:工程師小熊

摘要:用了很久的Git和svn,由於總是眼高手低,沒能靜下心來寫這些程序員日常開發最常用的知識點。現在準備開一個專題,專門來總結一下版本控制工具,讓我們從git開始。完成本系列博客的閱讀以後,你將掌握git的基本概念與git的基本命令,可以在本地隨心所欲的完成代碼的提交撤銷保存修改等操作、可以流暢的參與多人協作,本文致力於快速的入門,如果涉及到更高級的功能需要進行更深一步的學習。

本文核心點:

  • Git的基本概念
  • 一個人使用Git時的代碼版本控制--(提交、拉代碼、分支操作)
  • 多人合作時的代碼版本控制--(合併衝突、暫存代碼)

什麼是Git

簡介

git是世界上目前最先進的分佈式版本控制系統,致力於團隊、個人進行項目版本管理,完美的解決難以比較代碼、難以合併代碼、難以取消修改、難以在寫當前代碼的過程中保存未完成的修改去修改線上版本的bug等的痛點。

git是一個非常強大的工具,但作爲一個git使用者來說,不用完全學習Git的知識點與命令,因爲有的命令的使用頻率非常的低甚至數年都不會用到,讓我們來由淺入深進行學習。

git的歷史

git是linux的創始人linus,在付費版本控制工具BitMover收回對Linux社區免費使用權利的時候,一怒之下花費兩個星期的時間寫出來的。(牛筆的人)

開始

安裝git

選擇自己的操作系統對應的git版本安裝,安裝成功後運行git version後,輸出git版本則安裝正確。

git 官方: https://git-scm.com/downloads

配置用戶信息

使用git config命令來配置用戶名和郵箱

git config --global user.name "pzqu" 
git config --global user.email [email protected]
如果用了 --global 選項,那麼更改的配置文件就是位於你用戶主目錄下的那個,以後你所有的項目都會默認使用這裏配置的用戶信息。如果要在某個特定的項目中使用其他名字或者電郵,只要去掉 --global選項重新配置即可,新的設定保存在當前項目的 .git/config 文件裏。

使用git config user.namegit config user.email來檢查是否成功,也可以直接用git config --list來列出全部git配置信息來查看

img

創建git託管的項目

假如我們創建一個項目叫make_money,先創建一個文件夾叫make_money,再使用git init命令創建git項目。

# pzqu @ pzqu-pc in ~/Documents/code/test [0:05:29]
$ mkdir make_money

# pzqu @ pzqu-pc in ~/Documents/code/test [0:06:24]
$ ls
make_money

# pzqu @ pzqu-pc in ~/Documents/code/test [0:06:29]
$ cd make_money

# pzqu @ pzqu-pc in ~/Documents/code/test/make_money [0:07:10]
$ git init
Initialized empty Git repository in /Users/pzqu/Documents/code/test/make_money/.git/

# pzqu @ pzqu-pc in ~/Documents/code/test/make_money on git:master o [0:07:12]
$ ls -al
total 0
drwxr-xr-x  3 pzqu  staff   96 11  7 00:07 .
drwxr-xr-x  3 pzqu  staff   96 11  7 00:06 ..
drwxr-xr-x  9 pzqu  staff  288 11  7 00:07 .git

創建成功以後,會出現一個叫.git的隱藏文件夾,這個就是你的git倉庫,以後所有的git操作歷史提交記錄信息就全部記錄在此了,只要這個文件夾在就可以記住我們的全部git操作

工作區和暫存區

在使用git的時候還要清楚暫存區和工作區的含義,參考廖雪峯的官方網站-git篇-工作區和暫存區

常見情況

提交代碼

新文件與修改

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [11:37:50]
$ ls
README.md

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [11:42:02]
$ touch file1.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [11:42:15]
$ git add file1.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [11:42:23]
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   file1.txt
# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [11:56:38]
$ git commit -m "[+]add new file1.txt"
[master 66cc488] [+]add new file1.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file1.txt

上圖操作包含:

  • 創建新文件file1.txt
  • add 添加修改的內容到索引
  • status 查看修改的內容
  • commit 把索引提交到本地分支

git add . :監控工作區的狀態樹,此命令會把工作時的所有變化提交到暫存區,包括文件內容修改(modified)以及新文件(new),但不包括被刪除的文件。

git add -u:他僅監控已經被add的文件(即tracked file),他會將被修改的文件提交到暫存區。add -u 不會提交新文件(untracked file)。(git add --update的縮寫)

git add -A :是上面兩個功能的合集(git add --all的縮寫)

imgupload successful

git show 列出最近一次的提交
對於commit:像這樣,你不斷對文件進行修改,然後不斷提交修改到版本庫裏,就好比玩RPG遊戲時,每通過一關就會自動把遊戲狀態存盤,如果某一關沒過去,你還可以選擇讀取前一關的狀態。有些時候,在打Boss之前,你會手動存盤,以便萬一打Boss失敗了,可以從最近的地方重新開始。Git也是一樣,每當你覺得文件修改到一定程度的時候,就可以“保存一個快照”,這個快照在Git中被稱爲commit。一旦你把文件改亂了,或者誤刪了文件,還可以從最近的一個commit恢復,然後繼續工作,而不是把幾個月的工作成果全部丟失。

刪除文件

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [12:55:24]
$ ls
README.md file1.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [12:55:25]
$ git rm file1.txt
rm 'file1.txt'

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [12:55:30]
$ ls
README.md

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [12:55:32]
$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    deleted:    file1.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [12:55:40] C:128
$ git commit -m "[-]delete file1.txt"
[master e278392] [-]delete file1.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 file1.txt

上圖操作包含:

  • 創建新文件file1.txt
  • git rm 刪除file1.txt文件
  • status 查看修改的內容
  • commit 把索引提交到本地分支

tip1: 如果沒有用git rm刪除文件,在本地刪除文件後,git add一下再提交可以達到同樣的效果

tip2: 要是你加班太晚,頭暈不小心刪除了不想刪除的文件怎麼辦?見

下一篇:版本控制工具——Git常用操作(下)-後悔藥

拉代碼

方法一 pull

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [17:01:13]
$ git pull
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:pzqu/git_test
   5fd4d8f..7b54a8a  master     -> origin/master
Merge made by the 'recursive' strategy.
 share_file.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 share_file.txt

上圖命令:

  • git pull

查看本地倉庫變化git log

imgupload successful

上圖可以看到向遠程倉庫pull的時候,出現了兩個新的commit,commit 7b54a8ae74...的提交信息爲Create share_file.txt,另一個commit fdbb19cf4c51770的提交信息爲Merge branch 'master' of github.com:pzqu/git_test。事實上主線只有一個提交,爲什麼會出現這種情況? 是因爲pull其實會做兩個操作

  • 拉遠程倉庫代碼到本地
  • 自動與當前分支合併並生成一個合併成功的提交

注意這裏的第二個個步驟如果遠程有人和你改了同一個文件就會出現一個衝突,這個時候git會提示你哪些文件有衝突,手動改了再提交一次就可以了。詳情見合併衝突

方法二 fetch

我在遠程修改了文件,向share_file.txt加了一行內容tom modify,此時拉代碼。

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [21:07:21]
$ git fetch

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [21:08:43]
$ git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: [+]add new file1.txt
Applying: [-]delete file1.txt

上圖所示有以下兩個操作

  • fetch 拉取遠端代碼到本地
  • rebase 把本地代碼提交基於遠端分支重新replay

效果如下:

imgupload successful

上圖是git log所輸出的提交內容,剛剛pull的時候忘記把pull自動產生的merge提交到遠程,rebase的時候把本地的提交放到了遠程提交之後,看起來就是一條直線,比較優雅,也是推薦的方式。

同樣的,如果產生了衝突,詳情見合併衝突

分支操作

創建分支

分支是多人協同最經典的地方所在,我們來創建一個分支

$ git checkout -b dev/pzqu origin/master
Branch 'dev/pzqu' set up to track remote branch 'master' from 'origin'.
Switched to a new branch 'dev/pzqu'

$ git branch
* dev/pzqu
  master
  • git checkout -b 分支名 其他分支,-b代表創建並切換到新建的分支,分支名代表新創建的分支叫什麼名字,這裏叫dev/pzqu其他分支代表基於哪一個分支來創建,這裏基於遠程的master分支origin/master,如果省略則代表基於當前分支
  • git branch展示本地的分支情況,加-a參數可以展示全部的分支,包括遠程分支
  • *在分支前,指明瞭現在所在的分支是dev/pzqu

切換分支

$ git checkout -b dev/pzqu2
Switched to a new branch 'dev/pzqu2'

$ git branch
  dev/pzqu
* dev/pzqu2
  master

$ git checkout dev/pzqu
Switched to branch 'dev/pzqu'
Your branch is up to date with 'origin/master'.

$ git branch
* dev/pzqu
  dev/pzqu2
  master
  • 基於當前分支創建了一個新的分支並自動切換過去dev/pzqu2
  • git checkout 已存在的分支名切換分支回到dev/pzqu

刪除分支

$ git branch
* dev/pzqu
  dev/pzqu2
  master
  
$ git branch -D dev/pzqu2
Deleted branch dev/pzqu2 (was 7c9be37).

$ git branch
* dev/pzqu
  master
  
  • 位於dev/pzqu,刪除了dev/pzqu2分支

合併衝突

合併同一個分支的衝突(常見)

爲了產生一個衝突,我在另一個地方向遠程倉庫提交了代碼,更改share_file.txt文件,加了一行內容tom add for merge

本地修改同一個文件加了一行pzqu add for merge,並提交到本地,這樣一來,本地和遠程倉庫的同一個文件就不一樣了,一會拉代碼一定會產生一個衝突。效果如下:

imgupload successful

  • 一般rebase或pull衝突的時候,都會出現提示,然後git status會出現上圖圖示
  • 這個時候不可以進行任何分支切換和commit操作,按照他提示進行處理
  • git status提示哪個文件是都被修改的,both modified,然後使用編輯器修改該文件,解決衝突
  • 解決完成後,git add 添加該衝突文件
  • git rebase --continue,並更新commit message,完成整個rebase流程 我們來看看這個衝突的文件:

imgupload successful

Git用<<<<<<<=======>>>>>>>標記出不同分支的內容,我們修改如下後保存:

imgupload successful

git addgit rebase --continue後完成rebase,效果如下,再push的遠程倉庫即可

imgupload successful

合併不同分支的代碼產生衝突

關於怎麼創建分支與切換分支見創建分支和切換分支,這裏只討論合併時產生的衝突的情況,我們已經基於master分支創建了一個dev/pzqu分支

$ git branch
* dev/pzqu
  master

切換到master分支,加一行master add for merge並提交,文件內容如下:

$ cat share_file.txt
tom add
tom modify
tom add for merge
pzqu add for merge
master add for merge

切換到dev/pzqu分支,向share_file.txt加入一行dev/pzqu add for merge並提交,現在share_file.txt內容如下:

$ cat share_file.txt
tom add
tom modify
tom add for merge
pzqu add for merge
dev/pzqu add for merge

現在兩個分支的同一個文件內容不一樣了,現在我們在dev/pzqu分支上進行合併:

$ git merge master
Auto-merging share_file.txt
CONFLICT (content): Merge conflict in share_file.txt
Automatic merge failed; fix conflicts and then commit the result.

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:dev/pzqu x [11:17:31] C:1
$ git status
On branch dev/pzqu
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:   share_file.txt

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

$ cat share_file.txt
tom add
tom modify
tom add for merge
pzqu add for merge
<<<<<<< HEAD
dev/pzqu add for merge
=======
master add for merge
>>>>>>> master

上圖出現了一個衝突,是我們意料之中的,修改share_file.txt文件,解決此衝突:

$ cat share_file.txt
tom add
tom modify
tom add for merge
pzqu add for merge
dev/pzqu add for merge
master add for merge

$ git add share_file.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:dev/pzqu x [11:22:40]
$ git commit -m "[*]merge master to dev/pzqu"
[dev/pzqu d9e018e] [*]merge master to dev/pzqu

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:dev/pzqu o [11:23:00]
$ git status
On branch dev/pzqu
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

衝突解決也提交了,看看我們現在的分支內容:

imgupload successful

上圖我們可以看到:

  • master分支比遠程origin/master分支多一次提交,dev/pzqu分支由於是基於origin/master分支,合併了master分支的提交和當前dev/pzqu分支的提交,超出本地master兩個提交,致此我們把master合併到dev/pzqu的操作就完成了。
  • 通常我們開一個新的開發分支是爲了在自己的分支上寫代碼,方便提交也不會把主線弄亂,現在我們用同樣的方法將dev/pzqu合併到master分支,然後把兩個分支都提交到遠程。
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

$ git merge dev/pzqu
Updating 58f047a..d9e018e
Fast-forward
 share_file.txt | 1 +
 1 file changed, 1 insertion(+)

$ git push origin master
Total 0 (delta 0), reused 0 (delta 0)
To github.com:pzqu/git_test.git
   7c9be37..d9e018e  master -> master
   
$ git push origin dev/pzqu
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 887 bytes | 887.00 KiB/s, done.
Total 9 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), done.
remote:
remote: Create a pull request for 'dev/pzqu' on GitHub by visiting:
remote:      https://github.com/pzqu/git_test/pull/new/dev/pzqu
remote:
To github.com:pzqu/git_test.git
 * [new branch]      dev/pzqu -> dev/pzqu
  • 切換到master分支
  • 合併dev/pzqumaster分支
  • master推到遠程倉庫
  • 如果dev/pzqu要保留,就可以推送到遠程倉庫。

imgupload successful

  • 現在我們可以看到全部的分支都在一起了,強迫症都舒服了。

暫存代碼保存現場

這種情況一般是出現在你正在完成一個功能,但是忽然線上發現了一個Bug,必須馬上開一個新的分支來修復bug,但是現在的功能沒寫完不打算提交(commit),現在怎麼辦??不用怕暫存代碼來幫助你。

$ git status
On branch dev/pzqu
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   need_stash.txt
    modified:   share_file.txt

$ git stash
Saved working directory and index state WIP on dev/pzqu: d9e018e [*]merge master to dev/pzqu

$ git stash list
stash@{0}: WIP on dev/pzqu: d9e018e [*]merge master to dev/pzqu

$ git status
On branch dev/pzqu
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean


//省略操作:去創建一個Bug分支,修復他並完成與主線的合併,刪除Bug分支。
//省略操作:切回來當前分支繼續開發
//下面來恢復現場


$ git stash apply stash@{0}
On branch dev/pzqu
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   need_stash.txt

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:   share_file.txt
  • status查看到有2個文件修改沒有提交
  • stash把修改放到暫存區,並生成一個id
  • stash list列出暫存區所有內容
  • stash apply重新把暫存區內容放到本地

這裏的stash apply成功的把暫存區的一次暫存恢復到了本地,但是暫存區還有會保存這次暫存,如果想刪除這次暫存要用git stash drop來刪除;也可以用git stash pop,恢復最後一次暫存的同時把stash內容也刪了。

$ git stash drop stash@{0}
Dropped stash@{0} (bfdc065df8adc44c8b69fa6826e75c5991e6cad0)

$ git stash list

好了,暫存區清乾淨了。

​ 注意:要放到暫存區的文件一定要先通過git add加到index

小結

本文閱讀結束以後,我們學會了

  • Git的基本概念,知道git的作用、歷史;學會安裝配置Git,使用Git創建項目託管以及工作區和暫存區的概念
  • 學會Git的本地操作,提交、拉代碼、創建切換刪除分支操作,
  • 多人合作時的代碼版本控制,學會了不同情況下的合併衝突、暫存代碼操作

下集預告

Git常用操作(下)我計劃給大家介紹以下點:

  • 後悔藥-各種後悔操作(撤消commit,回滾,回退遠程倉庫等)
  • 哎呀,提交的時候漏了文件
  • tag操作
  • git忽略不想提交的文件

注意事項

理論上,git日常用到的命令是 diff show fetch rebase pull push checkout commit status 等,這些命令都不會導致代碼丟失,假如害怕代碼丟失,可以預先commit一次,再進行修改,但切記

不可使用自己不熟悉的命令 任何命令,不要加上-f的強制參數,否則可能導致代碼丟失

建議多使用命令行,不要使用圖形界面操作

引用

git官網

廖雪峯的官方網站-git篇

hexo博客部署到vps

此文已由騰訊雲+社區在各渠道發佈

獲取更多新鮮技術乾貨,可以關注我們騰訊雲技術社區-雲加社區官方號及知乎機構號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章