如何正常使用git
小金鱼A_suozhang需要一些记录来保证自己不会忘记这些命令
WorkFlow
Simplest Example
- 首先
git init
创建仓库 - 工作流(3棵树)
- 中间的是index(暂存区),是一个缓存区域
- 最后的是Head(也就是指向最后一次提交(Commit)的结果),到了head还不代表到了remote repository远程仓库
git add .
将当前目录的改动暂存(Stage)- 这个命令可能会将一些奇怪的东西添加进来,建议是用
git add -a
添加改动区(没staged的不加),然后git commit -a
- 这个命令可能会将一些奇怪的东西添加进来,建议是用
git commit -m "XXX"
将Index中的内容提交给headgit push
推送改动
分支 (Branch)
其产生是为了特性开发绝缘,创建仓库的时候,默认分支为“Master”
- 创建新的分支
git checkout -b feature_x
- 删除新的分支(当然你首先要切回主分支)
git branch -d feature_x
- 推送到当前分支
git push origin feature_x
- 删除新的分支(当然你首先要切回主分支)
- 切换回到主分支
git checkout master
更新与合并
- 更新本地仓库(Local Repo)到最新的改动
git pull
- 相当于在目前的目录中获取(Fetch)并且合并(Merge)了远端(Remote)的改动
- 这里的merge是默认fast forward的
- 如果你只想合并其他某个分支到你的当前分支
git merge branch
-
执行以上两者的时候都自动合并冲突(Conflict),有时候可能不成功,就要手动合并,修改完之后利用
git add <Filename>
来表征合并完毕,在合并的时候利用git diff <Source_Branch> <Target Branch>
- 替换本地文件,采用
git checkout <filename>
用head中的文件替换本地的文件(但是不会改变staged区域)- Tutorial Link
- 需要区分和reset和revert的区别
- revert 表示对public branch撤销commit,没有对文件操作的方式
- reset对文件操作是做Unstage,而对commit做表示撤销所有未经commit的变化
git checkout $FILENAME
是和直接和remote重置
- checkout对文件是改变working directory中的内容(不会改变stage区域),对commit做表示切换branch
- 对某个具体的commit可以specify commit,like
git reset HEAD~2
- 将Head复制到了Local Repo
git reset -- files
撤销最后一次git add- 也就是撤销stage区域
-
替换整个本地仓库为云端版本,先获取服务器的最新版本
git fetch origin
,将本地仓库指向它git reset --hard origin/master
- merge与rebase的区别
- 当执行git push的时候默认进行了merge
- 如果当只是本地的小改动的话可以使用
git rebase -i origin/XXX
- rebase完了还是要push的哦
- git pull和git fetch
- git pull相当于先fetch再merge
- git fetch下来只是更新了.git内部的,压缩过的新版本代码
- 这样就可以利用git diff来查看和远程的有什么区别了
- Dont Just Pull, Fetch & Merge
- 执行git merge之后如果出现冲突,git会直接在你的文件里修改,回去把冲突消除之后再add然后commit就好了
- Git Merge的两种情况 (Fast-Forward or not) 参考几个动图
- 默认是FF的,加上 –no-ff 不去ff
- FF:When Current Branch have no Commits, Merging branch
- DO-NOT Create a new commit
- 只是单纯的把新的branch连到了原来的branch上并且把HEAD指向其
- No-FF:如果当前的branch有新的commit
- 一般出现在我本地改动了一个Commit,但是别人也往Remote上commit了,导致现在的remote已经不是之前我本地的版本了
- Git creates a new active Node (Merging Commit)
- 当发生Merge CONFILCT的时候,我们需要手动Fix并且进行一个Commit,我们就手动创建了一个Merge Commit
- 对一个Branch的内容进行更新的办法除了Merge就是Rebase
- It copies the commit of current branch, and put it on top of the merging branch
- 但是,rebase会直接用现有的情况去覆盖merging branch的内容(因为首先要让merging branch会到Current branch commit之前的状态),因此不会有MERGE CONFLICT,也不会有Merge Commit节点,让整个开发树保持线性
- 因为很危险,所以一般要用rebase -i来进行控制
- 另外一种方法是Cherry-pick
- 如果对另一个branch的多个commit中只想要引入一个进来,如果merge的话会全都考虑进来,可以用
git cherry-pick COMMIT_ID
- 如果对另一个branch的多个commit中只想要引入一个进来,如果merge的话会全都考虑进来,可以用
- Merge-Conflict的example
- 比如说我先在本地做了修改并且commit且push了,push的时候报错表示我remote有了我本地没有的更改,pull下来之后发现conflict了,我们需要手动修复conflict
- 会出现这样的额外内容
- «« HEAD
- 我刚才修改的内容
- =========
- remote上别人修改的内容
- ======> remote_commit ID
补充(Complement)与回退(reset/checkout/revert)
git commit --amend
可以快捷修改当前的commit- 然后在amend出来的文件里添加第二次commit做了啥,保存之后退出
- 如果上一个commit没有push上去,那么会直接替代没有关系
- 如果上一个已经push上去了,由于需要覆盖会冲突,所以需要用
git push -f
回退 reset/checkout/revert
在Commit层面
- reset:将某个分支的末端指向某个提交
git reset HEAD~2
- 将当前branch的回退两个commit
- 有Soft和Hard两种,可以用
git reset --soft HEAD~2
来指定,soft的话只会移动HEAD的指向的位置,后面的两个Commit依然是可以Access的,但是hard的话最后的两个commit将被删除(也包括本地的Stage以及Working Directory中的新文件) * 相对危险,请只用于private repo
- checkout - 一般用于切换分支
- 除了换分支之外,也可以切换到别的提交
git checkout HEAD~2
- git会强制覆盖本地的所有更改,所以Git会要求你预先Stage所有的更改,某则都会丢失
- 除了换分支之外,也可以切换到别的提交
- revert相对安全,在撤销一个提交的时候会创建一个新的Commit,它并不会重写commit历史,只是找出一个commit,并且创建一个新的commit加入
git revert HEAD~2
- 保存了之前的所有,只是新加了一个
在File层面
- reset - 将缓存区(Stash)同步到你指定的Commit
git reset HEAD~2 foo.py
不会影响Working Directory中的文件 - checkout - 类似,就是改变Working Directory但是不改变Stash
查看信息
git status
git reflog
git log
查看以往的commit都做了什么 (如果需要比较精简的便于查找ID用git reflog
,然后通过reset获得神秘的时光机效果)git log --graph
git remote -V
- 没有什么卵用的功能
ls .git/refs/remotes/origin/
git branch -r
git show
` 查看当前commit的修改情况
Gitignore
Tempelate
- 可以使用Git官方提供的一些模板,可以排除掉Build所产生的文件
Place
- 就放在.git文件所在的目录
Usage
- 保存.gitginore之后可以使用
git status
来检查是否正确(Vscode直接用git框子里面看) - 也可以用
git check-ignore -v XXX
- 主要用到的 (支持逻辑通配)
- *.pth
- folder1/folder2
- 已经上传的,后修改了gitignore (清空缓存可以解决)
git rm -r --cached . add .
- 一些简单的模板
#注释 .gitignore的注释
*.txt 忽略所有 .txt 后缀的文件
!src.a 忽略除 src.a 外的其他文件
/todo 仅忽略项目根目录下的 todo 文件,不包括 src/todo
build/ 忽略 build/目录下的所有文件,过滤整个build文件夹;
doc/*.txt 忽略doc目录下所有 .txt 后缀的文件,但不包括doc子目录的 .txt 的文件
bin/: 忽略当前路径下的 bin 文件夹,该文件夹下的所有内容都会被忽略,不忽略 bin 文件
/bin: 忽略根目录下的 bin 文件
/*.c: 忽略 cat.c,不忽略 build/cat.c
debug/*.obj: 忽略debug/io.obj,不忽略 debug/common/io.obj和tools/debug/io.obj
**/foo: 忽略/foo, a/foo, a/b/foo等
a/**/b: 忽略a/b, a/x/b, a/x/y/b等
!/bin/run.sh 不忽略bin目录下的run.sh文件
*.log: 忽略所有 .log 文件
config.js: 忽略当前路径的 config.js 文件
/mtk/ 忽略整个文件夹
*.zip 忽略所有.zip文件
/mtk/do.c 忽略某个具体文件
- 可以直接用
git clone
将仓库拷贝到本地的其他位置
具体操作CheatSheet
对某个repo免密码push
- 打开该工程目录下的.git/config
- 将url改成
https://A-suozhang:$PASSWD@github.com/A-suozhang/$REPO_NAME
- 将url改成
- 设置全局的gitignore,首先执行
git config --global core.excludesfile ~/.gitignore
- 此时的
~/.gitconfig
`会变成这样(当然你手动加入指令也是可以的)
- 此时的
- 然后编辑根目录下的gitignore自动和本机的git绑定
- 同理可以在gitconfig中建立账户信息
git config --global user.email
免密码Token
git config --global credential.helper store
,密码会生成在~/.git-credential
暴力PUSH
- 因为各种操作(比如回退到了之前的Commit)可能会出现
Your Repo Is Behind master/branch by 2 commit, try git pull first
现在理论上是不能commit也不能push的,但是如果我们真的需要push git push --force
(谨慎啊)
强行回退版本
- 首先
git log
查看历史Commit,找到自己想要回到的那一次,记录下来COMMIT_ID - 执行
git reset --hard COMMIT_ID
- 相当于直接删除对应的commit
- 或者是
git revert COMMIT_ID
- 相当于用一次新的commit来回滚到之前的commit
Commit 完了之后发现还需要改个什么东西
git add $SOME_FILE
git commit --amend --no-edit
- (如果已经push到remote上,那么再
git push -f
)
Git Add之后需要撤销
git reset HEAD ./XXX
- 这样做的话不会实际改变文件,而
git checkout
则会丢弃本地修改 - This act means Unstage
- 这样做的话不会实际改变文件,而
测试更新与Branch
- 首先我新建一个文件
testGit.sh
git add .
过去,查看git status
显示新增了一个文件git reset --file testGit.sh
之后git status
发现显示目前还有testGit.sh这个文件没有提交
- Addh回来,Commit上去
git commit -m "Adding testGit.sh"
,同步到了HEAD - 新建一个branch
git checkout -b branch0
- 把HEAD push到新的branch上
git push origin branch0
- 切换回到master branch,
git checkout master
- 直接
git pull
,完成之后发现新的文件testGit没有了(合理,因为master branch上并没有这个文件) - 切换到
git checkout branch0
,然后git pull
但是失败了,尝试git pull origin branch0
,文件又回来了 - 执行
git diff master origin
对比几个branch git branch -vv
查看对每个branch做了什么git branch
查看当前的分支(带星号的是)git checkout master; git merge branch0
合并分支git branch -d branch0
删除合并后的分支
当简单修改的时候使用rebase来精简graph
这里没写清楚,看下面
- 这个一般般 - lxf教程
- Git Book
- 这个讲的很清楚
1. 当你修改了一个文件,add-commit完了想把它推到远程去,但是远程仓库已经被人修改了
2. 这个时候普遍操作是先pull下来,然后再merge,但是你下次commit的时候就会出现一个
merge comit
才能push上去 3. 与之替代,你可以git rebase origin/XXX
- 这个操作会把你之前的commit给取消掉,并且在新的origin后面append新的修改
- rebase的过程中会出现conflict,解决之后用git add,然后不用commit,可用
git rebase --continue
- 如果中途放弃,可以
git rebase --abort
- rebase的过程中会出现conflict,解决之后用git add,然后不用commit,可用
- 这个讲的很清楚
1. 当你修改了一个文件,add-commit完了想把它推到远程去,但是远程仓库已经被人修改了
2. 这个时候普遍操作是先pull下来,然后再merge,但是你下次commit的时候就会出现一个
Git Stash
当开发了一半,但是想要从remote同步代码的时候
- 先用
git stash
把内容暂存下来 - 然后可以正常的进行push和pull,commit (stash之后是可以正常的fetch merge的)
git stash pop
git stash drop
Git Show
想要查看某个commit的修改的时候
git log
查看commit的哈希值git show
查看最新的commitgit show COMMIT_ID
查看某个commit的修改git show COMMIT_ID FILENAME
查看某个commit的某个文件的修改
对于不是clone下来的新项目
- 正常的git add以及commit
git remote add https://XXX.git
git push -u origin master
Add Deletd Files while Metting “it does not exist”
- For single file, use
git add -u $SOME_FILE
- For the folder, use
git rm -r --cached $SOME_FOLDER
- Or
git add `git ls-files --deleted`
对于在不同的本地服务器之间进行仓库的同步
- 其实和正常flow基本一样,只是将原本的remote (可以用
git remote -v
来描述)- 将原本的git服务器上的(
https://github.com/A-suozhang./aw_nas
)切换成ztc.eva8.nics.cc:/home/zhaotianchen/code/aw_nas
- 将上面的连接加入git clone即可
- 两者同样需要commit与add
- 将原本的git服务器上的(
修改remote的地址
- 首先可以
git remote -v
看现在的remote是哪个地址-总之是个url,可以是和github服务器,也可以是本地服务器- 或者可以通过指定哪个remote(一般来说默认是origin)来查看对应的remote地址
git remote origin
- 同理push的时候也可以指定push到哪个remote
git remote set-url origin http://xxx.git
- 先删除再建立
git remote rm origin
git remote add origin https://
- 修改配置文件
- 进入
.git/config
- 或者可以通过指定哪个remote(一般来说默认是origin)来查看对应的remote地址
当你绑定了多个remote的时候
- 可以用
git remote -v
来查看remote的名字和url - 注意push的时候要指定push到哪一个remote
- pull的时候同理
- 注意当你的branch不在master的时候,如果你想要和某个remote同步,需要在pull时候指定remote和branch的名字
- 比如
git pull a-suozhang bnn
对于add以及commit不需要每个都add这件事
- 说起来非常惭愧到现在我才明白
- 在git status中
- changes not staged for commit 是不需要手动通过add加入的
- 只有下面的untracked file需要手动加入
- 在commit中指定-a就可以上面第一类中的所有内容全部给commit上去
rebase的理解
又一次发现自己之前没有搞清楚,我真的是个傻逼
一次新的开发例子
- 我现在需要回退几个commit,将它们合起来,同时这之间还会存在一个分支的split又merge的问题
- 如图所示,我需要回到add layer2 ss这个地方,并且保留add final model,将其他的commit全部合并成一个
git reflog
或者git log
来获得commit-id- 用
git rebase -i (COMMIT_ID)
- 其中COMMIT-ID是add final model的COMMIT-ID - 进入交互界面,(默认貌似是nano编辑器,可以用
git config --global core.editor "vim"
修改配置用vim) - 如图所示修改
- 它找出了在add final model前面(同时)的4个commit(Merge commit貌似被省略了)
- 下面的注释会告诉你如何操作,比如f的意思是fixup,跳过不显示commit内容,pick表示就是保留
- 在我们的case里:被pick下的是add controller这个commit,如果不修改名字的话上面的所有的commit就会被合成这个commit
- 如果出现了conflict(大概率),也就是你rebase的这个commit如果与现在的分支出现了conflict(肯定有,因为final model这个commit被我merge过),那么就会需要手动fix
- git add之后
git rebase --continue
就好了
- git add之后
-
在任何时候发现错了,可以直接
git rebase --abort
- 总之,git rebase就是将在指向commit之后的所有commit,合成一个commit,append在它后面
-
与之同等级的(不同的分支出去的),都认为在其之后,
-
- 现在完全看不懂我下面记的是什么吊东西
- 先checkout到某个branch,把其他branch的内容给rebase过来
强制回退版本
我感觉我好像在什么地方已经记过这个一次了
- git log查看COMMIT_ID
git reset --hard COMMIT_ID
- 如果使用
git checkout COMMIT_ID
会出现HEAD Detach的现象,导致不在任何一个branch上面
2021git更新了不让用密码而是用token进行登录
- since push needs token now, use this command to save the token after your push
git config --global credential.helper store
- 或者用ssh和git服务器进行登录,貌似可以避免科学上网(*)
- 但是maybe会出现其他问题
不去track某些文件
- 比如一些经常需要更新的train的配置文件,本来不应该每次git更新都更新,但是我又想保存这个文件
- 方案1:使用
git rm --cached
(这样做会直接在remote删除这个文件,这个时候看git status会显示deleted file)如果只用git rm
的话,甚至当前工作区中的也会被移除(并不满足我们的需求) - 使用
git update-index --skip-worktree kitti-train-mp.sh
- 但是要注意使用本命令的时候,working tree中本身不能有对该文件的修改,否则就会出现一些奇妙错误*,如果在其他分支出现了问题的话,就需要手动merge而且很复杂
- 所以以上的方式只能作为一个
.gitignore
的比较好的替代,但是好像还是不能很好的达到想不去track某些文件的作用,要是真的想这样,大概率还是用方案1
push到一个通过服务器之间ssh clone来的repo报错
- 本质原因是因为source repo不是一个bare-rep
- 我现在的case是我在eva3(eva3上的repo是从eva1上复制过来的)上修改了文件,我给它push到eva1上的时候报错了
Git Diff不出东西,但是不能mergre,说对local file做了修改?
-
- 本质原因是: 在远程的其他地方 delete了文件,但是本地仍然保留了
- 查看被ignore的file
git ls-files --ignored --exclude-standard -z
有时候从git上下载东西一直报错 curl Failed to connect to raw.githubusercontent.com port 443: Connection refuse
- 可能是DNS污染参考这个git issue
git status出一大堆文件,但是git diff文件并没有改动
- 参考这个
git config core.fileMode false