Git 快速入门指南

抛弃U盘,开始使用真正的版本控制系统。如果您遵循本指南,您可以在 30 分钟内开始使用 git!

如果您有任何编程经验,或者只是修改过配置文件,我确信您会被一个小的改动如何影响整个项目而震惊。在没有版本控制系统的情况下,识别和隔离问题通常非常耗时且耗费精力,需要回溯您的步骤并检查在首次出现意外行为之前所做的所有更改。版本控制系统的设计明确旨在简化该过程,并在文本版本之间提供可读的比较。

分布式版本控制系统(如 git)提供的另一个重要功能是横向移动的能力。传统上,程序员团队会线性地实现功能。这意味着从可信源(服务器)拉取代码,开发一个部分,然后将修改后的版本推送回服务器上游。使用分布式系统,每台计算机都维护一个完整的仓库,这意味着每个程序员都有完整的添加、删除和贡献者历史记录,以及回滚到以前版本或脱离可信仓库并派生开发树(我稍后会讨论)的能力。

快速入门指南

git 的优点在于您只需要了解很少的东西! 废话不多说,让我们从最重要的命令开始。

首先,我正在处理我之前的一个项目,该项目位于此处


[user@lj src]$ pwd
/home/lj/projects/java/spaceInvaders/src

要创建本地仓库,只需运行


[user@lj src]$ git init
Initialized empty Git repository in
 ↪/home/lj/projects/java/spaceInvaders/src/.git/

要将所有源文件递归添加到 git 的索引,请运行


[user@lj src]$ git add .

要将这些索引文件推送到本地仓库,请运行


[user@lj src]$ git commit

您将看到一个包含有关提交信息的屏幕,您可以在其中留下提交的描述


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
#
 Initial commit

 Changes to be committed:
        new file:   engine/collisionChecker.java
        new file:   engine/direction.java
        new file:   engine/gameEngine.java
        new file:   engine/gameObjects.java
        new file:   engine/level.java
        new file:   engine/main.java
        new file:   engine/mathVector.java
        new file:   graphics/drawer.java
        new file:   sprites/baseSprite.java
        new file:   sprites/boss.java
        new file:   sprites/enemy.java
        new file:   sprites/healthBar.java
        new file:   sprites/menu/menuItem.java
        new file:   sprites/menu/menuItemExclusiveMoveOnInput.java
        new file:   sprites/menu/menuItemLevelDecrease.java
        new file:   sprites/menu/menuItemLevelIncrease.java
        new file:   sprites/menu/menuItemMovementDirections.java
        new file:   sprites/menu/menuItemProjectileLimit.java
        new file:   sprites/menu/menuItemStartGame.java
        new file:   sprites/pickup/fireRateBoost.java
        new file:   sprites/pickup/pickup.java
        new file:   sprites/pickup/shield.java
        new file:   sprites/pickup/shieldPickup.java
        new file:   sprites/pickup/speedBoost.java
        new file:   sprites/player.java
        new file:   sprites/projectile.java
        new file:   sprites/wall.java


[user@lj src]$ git commit
[master (root-commit) 4cf5218]
 Initial commit
 Changes to be committed:
        new file:   engine/collisionChecker.java
        new file:   engine/direction.java
        new file:   engine/gameEngine.java
        new file:   engine/gameObjects.java
        new file:   engine/level.java
        new file:   engine/main.java
        new file:   engine/mathVector.java
        new file:   graphics/drawer.java
        new file:   sprites/baseSprite.java
        new file:   sprites/boss.java
        new file:   sprites/enemy.java
        new file:   sprites/healthBar.java
        new file:   sprites/menu/menuItem.java
        new file:   sprites/menu/menuItemExclusiveMoveOnInput.java
        new file:   sprites/menu/menuItemLevelDecrease.java
        new file:   sprites/menu/menuItemLevelIncrease.java
        new file:   sprites/menu/menuItemMovementDirections.java
        new file:   sprites/menu/menuItemProjectileLimit.java
        new file:   sprites/menu/menuItemStartGame.java
        new file:   sprites/pickup/fireRateBoost.java
        new file:   sprites/pickup/pickup.java
        new file:   sprites/pickup/shield.java
        new file:   sprites/pickup/shieldPickup.java
        new file:   sprites/pickup/speedBoost.java
        new file:   sprites/player.java
        new file:   sprites/projectile.java
        new file:   sprites/wall.java
 27 files changed, 2557 insertions(+)
 create mode 100755 engine/collisionChecker.java
 create mode 100755 engine/direction.java
 create mode 100755 engine/gameEngine.java
 create mode 100755 engine/gameObjects.java
 create mode 100755 engine/level.java
 create mode 100755 engine/main.java
 create mode 100755 engine/mathVector.java
 create mode 100755 graphics/drawer.java
 create mode 100755 sprites/baseSprite.java
 create mode 100755 sprites/boss.java
 create mode 100755 sprites/enemy.java
 create mode 100755 sprites/healthBar.java
 create mode 100755 sprites/menu/menuItem.java
 create mode 100755 sprites/menu/menuItemExclusiveMoveOnInput.java
 create mode 100755 sprites/menu/menuItemLevelDecrease.java
  create mode 100755 sprites/menu/menuItemLevelIncrease.java
 create mode 100755 sprites/menu/menuItemMovementDirections.java
 create mode 100755 sprites/menu/menuItemProjectileLimit.java
 create mode 100755 sprites/menu/menuItemStartGame.java
 create mode 100755 sprites/pickup/fireRateBoost.java
 create mode 100755 sprites/pickup/pickup.java
 create mode 100755 sprites/pickup/shield.java
 create mode 100755 sprites/pickup/shieldPickup.java
 create mode 100755 sprites/pickup/speedBoost.java
 create mode 100755 sprites/player.java
 create mode 100755 sprites/projectile.java
 create mode 100755 sprites/wall.java

文件以相同的 *NIX 风格从索引中删除


[user@lj src]$ git rm -r .
rm 'engine/collisionChecker.java'
rm 'engine/direction.java'
rm 'engine/gameEngine.java'

... SNIP ...

要将我的本地索引与仓库进行比较,我使用 git diff(注意:为了提高可读性,大多数行末尾的连字符行已被修剪)


[user@lj src]$ git diff --cached --stat
 engine/collisionChecker.java                   | 281 ----- ...
 engine/direction.java                          |  14 ----
 engine/gameEngine.java                         | 504 ----- ...
 engine/gameObjects.java                        |  61 ----- ...
 engine/level.java                              | 134 ----- ...
 engine/main.java                               |  51 ----- ...
 engine/mathVector.java                         |  46 ----- ...
 graphics/drawer.java                           | 323 ----- ...
 sprites/baseSprite.java                        | 303 ----- ...
 sprites/boss.java                              |  73 ----- ...
 sprites/enemy.java                             | 119 ----- ...
 sprites/healthBar.java                         |  64 ----- ...
 sprites/menu/menuItem.java                     |  21 ----- ...
 sprites/menu/menuItemExclusiveMoveOnInput.java |  31 ----- ...
 sprites/menu/menuItemLevelDecrease.java        |  28 ----- ...
 sprites/menu/menuItemLevelIncrease.java        |  27 ----- ...
 sprites/menu/menuItemMovementDirections.java   |  30 ----- ...
 sprites/menu/menuItemProjectileLimit.java      |  31 ----- ...
 sprites/menu/menuItemStartGame.java            |  33 ----- ...
 sprites/pickup/fireRateBoost.java              |  39 ----- ...
 sprites/pickup/pickup.java                     |  77 ----- ...
 sprites/pickup/shield.java                     |  39 ----- ...
 sprites/pickup/shieldPickup.java               |  47 ----- ...
 sprites/pickup/speedBoost.java                 |  38 ----- ...
 sprites/player.java                            |  64 ----- ...
 sprites/projectile.java                        |  44 ----- ...
 sprites/wall.java                              |  35 ----- ...
 27 files changed, 2557 deletions(-)

当不使用 cached 选项时,只会显示未添加到索引的更改。 stat 选项提供了一个快速表格,而不是通过 less 命令提供的标准逐行审查。 这在查找许多文件的摘要而不是分析小更改时通常很有用。

git status 提供更详细的输出,类似于 diff


[user@lj src]$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    engine/collisionChecker.java
        deleted:    engine/direction.java
        deleted:    engine/gameEngine.java

... SNIP ...

分支是 git 的重要组成部分,理解它们对于掌握 git 的真正运作方式至关重要。 每个分支都是不同的开发路径,也就是说所有分支都是相互独立的。 为了更好地解释这一点,让我们看一个例子。

所有项目都从 “master” 分支开始。 您可以使用以下命令查看当前分支


[user@lj src]$ git branch -a
* master

新分支用于开发功能,然后可以将其合并回 master 分支。 这样,不同的模块可以保持分离状态,直到它们稳定并准备好与 master 分支合并,所有其他分支都应从 master 分支派生。 这样,master 分支更像是树干而不是分支。 创建新分支通常被称为“派生”开发树,将线性开发分成两个,然后在派生分支上的开发完成后再次合并分支。

要创建新分支,并包含当前分支的所有数据,请使用


[user@lj src]$ git checkout -b development
Switched to a new branch 'development'

checkout 用于在从当前分支分离之前完成对当前分支的所有操作。 -b 开关从当前分支创建一个新分支。 在本例中,我创建了一个开发分支,其中将包含 “hot” 或不稳定代码。

接下来,将 master 分支重命名为 “stable”


[user@lj src]$ git branch -m master stable

现在您已经设置了两个分支,让我们比较它们以确保开发分支已填充 stable 分支的数据


[user@lj src]$ git diff --stat development stable
[user@lj src]$

由于 diff 没有返回任何内容,因此您知道它们包含完全相同的数据。

现在您已经设置了两个分支,让我们开始在开发分支上进行更改


[user@lj src]$ git diff --cached
diff --git a/engine/main.java b/engine/main.java
index 38577b5..5900d80 100755
--- a/engine/main.java
+++ b/engine/main.java
@@ -11,6 +11,8 @@ import graphics.drawer;

 public class main
 {
+       // WHEN CAN I GO BACK TO C!?!?!?
+
        /*
         * Class:                       main
         * Author:                      Patrick

并将它们提交到分支


[user@lj src]$ git commit
[development e1f13bd]  Changes to be committed: modified:
 ↪engine/main.java
 1 file changed, 2 insertions(+)

提交更改后,您将获得一个唯一的标识符——在本例中为 “e1f13bd”。 它用于引用此提交; 但是,对于脆弱的人类大脑来说,记住这样的东西非常困难,所以让我们用更易于记忆的东西标记它。

首先,查找并标记初始提交


[user@lj src]$ git log
commit e1f13bde0bbe3d64f563f1abb30d4393dd9bd8d9
Author: user <user@lj.linux>
Date:   Wed Jun 6 15:51:18 2018 +0100

     Changes to be committed:
            modified:   engine/main.java

commit 4cf52187829c935dac40ad4b65f02c9fb6dab7ba
Author: user <user@lj.linux>
Date:   Tue Jun 5 21:31:14 2018 +0100

     Initial commit
     Changes to be committed:
            new file:   engine/collisionChecker.java
            new file:   engine/direction.java
            new file:   engine/gameEngine.java
... SNIP ...



[user@lj src]$ git tag v0.1 4cf52187829c935dac40ad4b65f02c9fb
↪6dab7ba
[user@lj src]$ git tag v0.11 e1f13bd

现在两个提交都已用版本号标记,它们更容易记住和比较


[user@lj src]$ git diff --stat v0.1 v0.11
 engine/main.java | 2 ++
 1 file changed, 2 insertions(+)

在测试并确保开发分支稳定后,您应该将 stable 分支更新为当前的开发分支


[user@lj src]$ git checkout stable
Switched to branch 'stable'
[user@lj src]$ git merge development
Updating 4cf5218..e1f13bd
Fast-forward
 engine/main.java | 2 ++
 1 file changed, 2 insertions(+)

如下所示,两个分支仍然存在并且包含相同的数据


[user@lj src]$ git branch -a
  development
* stable
[user@lj src]$ git diff --stat stable development

如果您在任何时候想要回滚,请使用 git revert 来还原提交并撤消它所做的任何更改


[user@lj src]$ git revert v0.11
[stable 199169f] Revert " Changes to be committed:"
 1 file changed, 2 deletions(-)
[user@lj src]$ git diff --stat stable development
 engine/main.java | 2 ++
 1 file changed, 2 insertions(+)

这就是您开始并熟练地将 git 用于个人用途所需了解的全部内容。 所有 git 仓库的运作方式都相同; 但是,使用其他人拥有的远程仓库会带来其自身的注意事项。 请继续阅读。

使用远程仓库

首先,让我们获取远程仓库


[user2@lj ~]$ git clone src repo
Cloning into 'repo'...
done.

在 user2 进行一些更改后,它们被提交到 user2 的仓库


[user2@lj repo]$ git commit -a
[stable 6a04336]  Changes to be committed: modified:
 ↪graphics/drawer.java
 1 file changed, 1 insertion(+)

现在,原始所有者可以将更改拉回主仓库。 在可能的情况下,拉取会在一个命令中获取并合并更改


[user@lj src]$ git pull /home/user2/repo
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From /home/user2/repo
 * branch            HEAD       -> FETCH_HEAD
Updating 199169f..6a04336
Fast-forward
 graphics/drawer.java | 1 +
 1 file changed, 1 insertion(+)
[user@lj src]$ git diff --cached

所有更改都已拉入主仓库,并且双方都拥有代码的最新版本。 这在一个系统上的工作方式与在托管站点(如 GitHub)上的工作方式完全相同。 唯一的区别是仓库是通过 URL 而不是系统路径定位的。

这仅仅是 git 的开始! 本快速入门指南对于任何有抱负的开发人员或厌倦手动回滚版本的沮丧的程序员来说都应该足够了。 现在您可以称自己为 gitter,或简称 git!

Patrick Whelan 是英国 Edge Hill 大学二年级学生。 他是一位有抱负的开发人员、博主和全能黑客。

加载 Disqus 评论