常用命令
获取所有文件名: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 log --oneline
git log --pretty=oneline
分支
分支的用处体现在过程,而不是结果
创建新分支:git branch <branchname>
创建(并切换到)新分支:git checkout -b [-b|-B|--orphan] <new_branch>
,等价于:
切换分支:
git checkout <branch name>
git switch <branch name>
合并
合并方式
rebase
git rebase -i <commit|branch>
将当前分支移植到指定的 commit
或 branch
上,具体来说,就是从当前分支和目标分支的共同祖先开始,将当前分支接到目标分支后面
不会产生合并节点,不会保留分支结构,可以保证一条清晰的代码历史
merge
可能会产生新的无意义的合并节点,可能会在历史记录中保留分支结构
合并策略
git是为每个文件创建一个整体快照,而不逐行分析文件内容,因此,如果在两个分支上修改同一个文件就会在合并时发生冲突(尽管修改的是完全不相关的行,也会发生冲突)
冲突处理
git的冲突是按代码段来算的,如果两个分支修改了同一段代码(中间没有空行)(连续相邻的行称为同一段代码),那么合并时就会发生冲突,需要手动解决。
-
解释:相邻的行通常为一个整体,执行一段连续的逻辑操作。如果直接合并相邻行的更改,可能就会导致一些逻辑错误。ref
-
显然:同一文件中,连续相邻行的修改会被git视为一个修改(1 change),
因此,只要两条分支不是在同一个地方发生的change,就不会产生冲突,因为git将相邻行视为同一个上下文(context),这也是为了避免发生上述逻辑错误。
这样的设计虽然会降低一点实用性,但可以避免更大的风险。
git mv
实际上做了2个更改:
- 删除原文件
- 新建内容一样的文件
再将这两个操作添加(add)到暂存区等待提交(commit)
以下两组命令都等价于git mv
:
mv a b
git add a b
cp a b
rm a
git add a b
注:不论哪种方式,恢复的话,都需要手动删除新文件
修改历史版本
修改提交消息(commit message)
-
git commit --amend
修改最近的一次commit message -
git rebase -i HEAD~n
修改最近的n次commit message(doc)
修改文件内容
git rebase -i HEAD~n
# -i, --interactive 交互模式
- 执行命令后会自动打开一个文本编辑器,每行代表一个commit
- 行的顺序可看作栈,最近n个提交由近及远依次入栈,第一行(栈顶)为最远的提交
- 在要修改的commit行首,把
pick
改为edit
- 保存退出
- 这时HEAD会指向第一个edit行对应的commit,即工作区的内容会切换到相应的commit
-
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
- 修改文件
-
git add
添加到暂存区 - (可以跳过,直接第10步)
git commit --amend [-m 'message']
重新提交,形成一次新的commit,Hash值与原来不同(可多次执行,每次都会生成新的Hash值,但不会再增加commit) -
git rebase --continue
执行下一个rebase操作,即第5步 - 全部完成后,HEAD恢复到master
切换版本
术语常用的中英对照:
-
工作区:working tree,working directory
-
暂存区:stage,index
- reset
同时更改HEAD和其指向的分支,但不改动工作区(能看到的所有文件和目录),
仅撤销commit。撤销commit。--mixed
默认操作;不删除工作区;所有改动放入工作区,如果冲突,则按照以下优先级:工作区>暂存区>commit的内容,如果高优先级的区域没有内容,则把低优先级的东西放进去。commit和当前index内容放入工作区;撤销add(index),即不保留reset前(还未提交)的暂存区--soft
不删除工作区;暂存区以上的改动全部保留到暂存区,优先级同上。保留index的内容。commit内容放入暂存区;保留当前的index内容不撤销add,即保留reset前(还未提交)的暂存区--hard
删除工作区,删除暂存区。直接舍弃commit内容
-
checkout
仅移动HEAD(不变动分支)
git checkout [<branch>]
将HEAD指向[<branch>]
所指向的节点git checkout [<sha1>]
恢复某一个版本的某一个文件:
git checkout [版本] [文件]
将指定版本中的指定文件复制到工作区,并添加到暂存区,分支和HEAD都不会发生变化 -
版本快捷写法
^
代表上一个版本,^^
依次类推:-
HEAD^
HEAD上一个版本 -
HEAD^^
HEAD^的上一个版本 -
HEAD^^^
以此类推……
^
太多时,可以写为~n
,等价于n个^
:~3
等价于^^^
。HEAD~3
HEAD往前数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 forgit fetch
followed bygit merge FETCH_HEAD
.– Git documentation for
git pull
即,git pull
等于执行了下面两条命令:
git fetch
git merge
行尾
在Windows下打开git项目,行尾可能出现^M
,这是由于不同平台对行尾的处理不一致
git config --global core.autocrlf true
:
- 检查代码时:自动转换为
crlf
- 提交时:自动转换
crlf
为lf
拒绝提交包含混合换行符的文件:git config --global core.safecrlf true
git add --renormalize .
:将已记录的(tracked)文件,按照core.autocrlf
进行修正
Configuring Git to handle line endings
.gitignore
.gitignore
will prevent untracked files from being added (without anadd -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.
将文件从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).
查看文件的历史版本
git show [commit:]<file>
没有指定commit
就会显示最后一次改动