Git

发布于 2021 年 05 月 07 日 | 更新于 2022 年 03 月 12 日

常用命令

获取所有文件名:git ls-files

重命名文件:git mv

只提交更新(忽略新增文件):git add -u .

列出(满足各种条件的)文件名:git ls-files -c|--cached|-d|-deleted|-m|--modified|...

git show <Commit Hash>查看该commit的信息,包括:做了哪些修改

git status --porcelain查看未提交的文件,包括工作区和暂存区

打印单行历史记录:

分支

分支的用处体现在过程,而不是结果

Git分支管理案例

创建新分支:git branch <branchname>

创建(并切换到)新分支:git checkout -b [-b|-B|--orphan] <new_branch>,等价于:

切换分支:

合并

合并方式

rebase

git rebase -i <commit|branch> 将当前分支移植到指定的 commitbranch 上,具体来说,就是从当前分支和目标分支的共同祖先开始,将当前分支接到目标分支后面

不会产生合并节点,不会保留分支结构,可以保证一条清晰的代码历史

merge

可能会产生新的无意义的合并节点,可能会在历史记录中保留分支结构

合并策略

这才是真正的GIT——分支合并

git是为每个文件创建一个整体快照,而不逐行分析文件内容,因此,如果在两个分支上修改同一个文件就会在合并时发生冲突(尽管修改的是完全不相关的行,也会发生冲突)

冲突处理

git的冲突是按代码段来算的,如果两个分支修改了同一段代码(中间没有空行)(连续相邻的行称为同一段代码),那么合并时就会发生冲突,需要手动解决。

  1. 解释:相邻的行通常为一个整体,执行一段连续的逻辑操作。如果直接合并相邻行的更改,可能就会导致一些逻辑错误。ref

  2. 显然:同一文件中,连续相邻行的修改会被git视为一个修改1 change),因此,只要两条分支不是在同一个地方发生的change,就不会产生冲突,因为git将相邻行视为同一个上下文context),这也是为了避免发生上述逻辑错误。

这样的设计虽然会降低一点实用性,但可以避免更大的风险。

git mv

实际上做了2个更改

  1. 删除原文件
  2. 新建内容一样的文件

再将这两个操作添加(add)到暂存区等待提交(commit)

以下两组命令都等价于git mv

mv a b
git add a b
cp a b
rm a
git add a b

:不论哪种方式,恢复的话,都需要手动删除新文件

修改历史版本

修改提交消息(commit message)

官方文档

  1. git commit --amend修改最近的一次commit message

  2. git rebase -i HEAD~n修改最近的n次commit message(doc)

修改文件内容

git rebase -i HEAD~n
# -i, --interactive  交互模式
  1. 执行命令后会自动打开一个文本编辑器,每行代表一个commit
  2. 行的顺序可看作栈,最近n个提交由近及远依次入栈,第一行(栈顶)为最远的提交
  3. 在要修改的commit行首,把pick改为edit
  4. 保存退出
  5. 这时HEAD会指向第一个edit行对应的commit,即工作区的内容会切换到相应的commit
  6. git status可查看当前rebase的进度,如下:
➜ U_Net git:(cb97492) git status
interactive rebase in progress; onto 3422ca4
Last command done (1 command done):
   edit cb97492 Experiment: myLSTM/25
Next commands to do (2 remaining commands):
   pick 09036e8 Experiment: myLSTM/26
   pick f27ebaf Experiment: myLSTM/27
  1. 修改文件
  2. git add添加到暂存区
  3. (可以跳过,直接第10步)git commit --amend [-m 'message']重新提交,形成一次新的commit,Hash值与原来不同(可多次执行,每次都会生成新的Hash值,但不会增加commit)
  4. git rebase --continue执行下一个rebase操作,即第5步
  5. 全部完成后,HEAD恢复到master

切换版本

术语常用的中英对照:

  1. checkout

    仅移动HEAD(不变动分支)

    git checkout [<branch>]HEAD指向[<branch>]所指向的节点

    git checkout [<sha1>]

    恢复某一个版本的某一个文件:git checkout [版本] [文件]将指定版本中的指定文件复制到工作区,并添加到暂存区,分支和HEAD都不会发生变化

  2. 版本快捷写法

    ^代表上一个版本,^^依次类推:

    1. HEAD^HEAD上一个版本

    2. HEAD^^HEAD^的上一个版本

    3. HEAD^^^以此类推……

    ^太多时,可以写为~n,等价于n个^~3等价于^^^

    HEAD~3HEAD往前数3个版本,不算HEAD(HEAD就是当前版本,不需要切换)

小结:如果只是想运行某个版本的代码,就用checkout,这样方便返回最新版本

远程仓库

remote

查看远程仓库信息:git remote -v

更新远程仓库信息:git remote update [-p|--prune],prune表示移除已经不存在于远端的分支;好像和git fetch效果一样?

关联远程库git remote add <origin> <git@server-name:path/repo-name.git>

删除已关联的远程库git remote rm <origin>

<origin>为远程仓库的名字,通常为origin

upstream

查看本地和远端的所有分支:git branch -a|--all

查看分支的远端上游(upstream):git branch -vv

设置分支的远端上游(upstream):git branch -u <origin>,其中<origin>用于指定远端仓库(常用origin),具体的远端仓库分支名与本地分支名一致

各分支推送到远端的行为是独立的,即,执行git push只会推送当前分支到指定的upstream,不影响其他分支

git pull

Incorporates changes from a remote repository into the current branch. In its default mode, git pull is shorthand for git fetch followed by git merge FETCH_HEAD.

– Git documentation for git pull

即,git pull等于执行了下面两条命令:

git fetch
git merge

行尾

在Windows下打开git项目,行尾可能出现^M,这是由于不同平台对行尾的处理不一致

git config --global core.autocrlf true

拒绝提交包含混合换行符的文件:git config --global core.safecrlf true

git add --renormalize .:将已记录的(tracked)文件,按照core.autocrlf进行修正

End-of-line conversion

Configuring Git to handle line endings

git-diff to ignore ^M

.gitignore

.gitignore will prevent untracked files from being added (without an add -f) to the set of files tracked by Git, however Git will continue to track any files that are already being tracked.

How can I make Git “forget” about a file that was tracked, but is now in .gitignore?

.gitignore只对未记录的(tracked)文件有效

如果要将已记录的文件加入.gitignore(停止记录某些文件,在下个版本中,这些文件会被删除),可以使用以下命令:

git rm --cached <file>
git rm -r --cached <folder>

--cached: Use this option to unstage and remove paths only from the index. Working tree files, whether modified or not, will be left alone.

git-rm

将文件从index删除,不会删除磁盘上的文件,只是使仓库停止对该文件的记录,提交后(下个版本)生效

中文显示

git config --global core.quotepath false:不转义汉字等字符,直接显示汉字

Commands that output paths (e.g. ls-files, diff), will quote “unusual” characters in the pathname by enclosing the pathname in double-quotes and escaping those characters with backslashes in the same way C escapes control characters (e.g. \t for TAB, \n for LF, \\ for backslash) or bytes with values larger than 0x80 (e.g. octal \302\265 for “micro” in UTF-8).

core.quotePath

查看文件的历史版本

git show [commit:]<file>

没有指定commit就会显示最后一次改动