git working directory/index/stage/master
The index is also called stage, git add
will update index contents. The index actually stores only the file hash IDs. The file data, the actual contents of each file, are kept in Git’s objects, inside the repository (master).
What is the index anyway?
- the index mainly serves as a way for Git to stage “the next commit” one piece at a time, so that git commit itself is very fast.
- Git can switch to that commit (use
git checkout dev
,git checkout master
) on that branch by comparing that commit’s files to those in the current index. It only needs to change out files that are different. - When using
git checkout -b dev
, git will take index contents to another branch dev. So make sure the index is clean.
在github新建分支安全操作
Doing carefully is better than fixing after errors happen. Another saying is that discretion is the better part of valor.
git checkout -b <branch_name> 新建分支并将当前分支带到新建分支,git branch <branch_name>和git checkout <branch_name>
git add -A
git commit -m "×××××"
git push
git push --set-upstream origin <branch_name> push到新建分支上
git checkout master check到master分支
git merge <branch_name> 合并修改 <branch_name>和master指向同一个修改,merge后的修改,如果冲突需要手工修改
git branch -d test 删除本地分支
git push origin --delete <branch_name> 删除远程分支(github)上的分支
windows中git配置代理
$ git config --global http.proxy http://127.0.0.1:8087
$ git config --global https.proxy https://127.0.0.1:8087
使用https链接
$ git remote add origin https://github.com/username/××.git
版本回退
- Just modify file in working directory but not add to temporary directory
git checkout -- <filename> // undo modified
git clean -xdf // delete new files
- Add to temporary directory but not commit
//unstage modification,如果是全部文件使用 . 符号;
//使用--代替HEAD,对于没有commit的仓库
git reset HEAD 'filename'
git checkout -- 'filename'
- Commit finished
git log
git reset --hard HEAD^
保存当前分支修改
使用下面的命令可以在不commit当前分支的基础上,切换到另外一个分支
git stash
切回主分支(stash所在分支),然后键入git stash pop
将当前在master下的修改push到dev分支上
git stash // Hide away your changes (basically making a temporary commit),
git checkout {another-branch}
git pop // Pop re-applies them
如果整个work tree都乱了,那就
# Unstage everything (warning: this leaves files with conflicts in your tree)
git reset
# Add the things you *do* want to commit here _manually_ using gedit or vim
git add -p # or maybe git add -i
git commit
如果只是某几个文件无法自动merge,那就
git checkout -- <filename1>
# Add the things you *do* want to commit here _manually_ with gedit or vim
git add -p # or maybe git add -i
git commit
由于original-branch分支还没有处理,现在切回原来分支:
# The stash still exists; pop only throws it away if it applied cleanly
git checkout original-branch
git stash pop
# Add the changes meant for this branch
git add -p
git commit
# And throw away the rest
git reset --hard
git push and merge local branch
When push.default is set to ‘matching’, git will push local branches to the remote branches that already exist with the same name.
当 push.default 的值设置成 ‘matching’ ,git 将会推送所有本地已存在的同名分支到远程仓库
从 Git 2.0 开始,git 采用更加保守的值’simple’,只会推送当前分支到相应的远程仓库,’git pull’ 也将值更新当前分支。
git config --global push.default simple //同步当前分支
git config --global push.default matching //同步与当前工作区相同名称的所有分支
git删除远程或本地分支
Deleting a remote branch:
git push origin --delete <branch> # Git version 1.7.0 or newer
git push origin :<branch> # Git versions older than 1.7.0
Deleting a local branch:
git branch --delete <branch>
git branch -d <branch> # Shorter version
git branch -D <branch> # Force delete un-merged branches
.gitignore
1. .gitignore uses globbing patterns to match against file names
Pattern | Example matches | Explanation* |
---|---|---|
**/logs | logs/debug.log logs/monday/foo.bar build/logs/debug.log |
You can prepend a pattern with a double asterisk to match directories anywhere in the repository. |
/debug.log | debug.log but not logs/debug.log |
Prepending a slash matches files only in the repository root. |
debug.log | debug.log logs/debug.log |
By default, patterns match files in any directory. |
logs | logs logs/debug.log logs/latest/foo.bar build/logs build/logs/debug.log |
If you don’t append a slash, the pattern will match both files and the contents of directories with that name. In the example matches on the left, both directories and files named logs are ignored. |
logs/ | logs/debug.log logs/latest/foo.bar build/logs/foo.bar build/logs/latest/debug.log |
Appending a slash indicates the pattern is a directory. The entire contents of any directory in the repository matching that name – including all of its files and subdirectories – will be ignored |
And more details can refer to the link.
2. Ignoring a previously committed file
If you want to ignore a file that you’ve committed in the past, you’ll need to delete the file from your repository and then add a .gitignore
rule for it. Using the --cached
option with git rm
means that the file will be deleted from your repository, but will remain in your working directory as an ignored file.
$ echo debug.log >> .gitignore
$ git rm --cached debug.log
rm 'debug.log'
$ git status
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: debug.log
Untracked files:
(use "git add <file>..." to include in what will be committed)
debug.log<br/>
$ git commit -m "Start ignoring debug.log"
You can omit the --cached
option if you want to delete the file from both the repository and your local file system.
Git Submodule
You want to be able to treat the two projects as separate yet still be able to use one from within the other.This lets you clone another repository into your project and keep your commits separate. If your project contains other libraries which you never or seldom modify, you should adopt submodule to manage your project. And if one of subdirectories is independent to others, which means the subdirectory is special or distinguished, such as communication message subdirectory, you could employ that to it.
Cloning a Project with Submodules
If a new submodule is created by one person, the other people in the team need to initial this submodule. First you have to get the information about the submodule, this is retrieved by a normal git pull
. If there are new submodules you’ll see it in the output of git pull
. Then you’ll have to initial them with:git submodule init
.
If someone updated a submodule, the other team-members should update the code of their submodules. This is not automatically done by git pull, because with git pull it only retrieves the information that the submodule is pointing to another commit, but doesn’t update the submodule’s code. To update the code of your submodules, you should run:git submodule update
.
The above two commands can be combined to one command: git submodule update --init
.
Initialize your local configuration file and fetch all the data from that project. Another way is to pass --recurse-submodules
to the git clone command, it will automatically initialize and update each submodule in the repository.
git clone --recurse-submodules git@github.com:bryanibit/DGPS.git
Add config to git status of submodules
git config (--global) status.submoduleSummary true
Basic Operation (clone push merge and remove)
Add submodule to repoA and update it from repoB
A $ git submodule add <url> <path> # staged .gitmodules and a subdir
A $ git commit -m "add submodule"
A $ git push
B $ git submodule init # register .gitmodules
B $ git submodule update # append files to subdir
# at this time, B) subdir is HEAD detached status like commitID (fe64799)
When we’ve run the git submodule update
command to fetch changes from the submodule repositories, Git would get the changes and update the files in the subdirectory but will leave the sub-repository in what’s called a detached HEAD state. This means that there is no local working branch (like “master”, for example) tracking changes. With no working branch tracking changes, that means even if you commit changes to the submodule, those changes will quite possibly be lost the next time you run git submodule update. You have to do some extra steps if you want changes in a submodule to be tracked.
In order to set up your submodule to be easier to go in and hack on, you need to do two things. You need to go into each submodule and check out a branch to work on. The options are that you can merge them into your local work, or you can try to rebase your local work on top of the new changes.
$ git checkout stable
$ git submodule update --remote --merge
The foregoing commands means that you should create a new branch to save your modifications if you want to modify submodules. If not, those changes will be lost the next time running git submodule update.
Modify submodule in repoA and update it from repoB
A $ git commit -am "modify submodule" # in submodule
A $ git push # in submodule
A $ git commit -m "update submodule" # in root
A $ git push # That needs two commits and two necessary pushes
B $ git pull # in root, git status shows "following shown SH" at this time
B $ git submodule update # in root, subdir is still in HEAD Detached like another commitID (5e73195)
The shown SH is:
B (master * u=) $ git status
On branch master
Your branch is up-to-date with 'origin/master'
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: vendor/plugins/demo (new commits)
Submodules changed but not updated:
* vendor/plugins/demo 0e90143...fe64799 (2):
< Pseudo-commit #2
< Pseudo-commit #1
no changes added to commit (use "git add" and/or "git commit -a")
In SH, The current prompt, with its asterisk (*), does hint at local modifications, because our working directory is not in sync with the index, the latter being aware of the newly referenced submodule commit IDs. The angle brackets point left (<)? Git sees that the current WD does not have these two commits, contrary to the container project’s expectations.
Push changes in submodules
The above said we need two commits and two necessary pushes. If not, there will be fatal: reference is not a tree
, because of lack of push submodules.
So the following commands can make sure one push with some arguments.
$ git push --recurse-submodules=check
The “check” option will make push simply fail if any of the committed submodule changes haven’t been pushed. It is deployed after git push each submodule.
$ git push --recurse-submodules=on-demand
Git went into the submodule and pushed it before pushing the main project. If that submodule push fails for some reason, the main project push will also fail.
$ git push
In each submodule and then main project.
If you think it is too nagging, append the config to git: git config --global alias.spush 'push --recurse-submodules=on-demand
At last but not least, how to removing a submodule, please refer to medium of Christophe Porteneuve.
The left content is how to solve the HEAD detached status:
- Directly method
subdir ((remotes/origin/HEAD)) $ git checkout master Previous HEAD position was 0e90143... Pseudo-commit
Switched to branch 'master' Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
subdir (master u-2) $ git pull --rebase First, rewinding head to replay your work on top of it... Fast-forwarded master to 0e9014309fe6c663e806c9f91297a592ee04cb6c. demo (master u=) $
- Simple way
B) (master u=) $ git submodule update --remote --rebase -- <subdir>
git submodules alternatives
Repo
Repo is a tool created by Google to manage the rather large Android project, which is spread across multiple different Git project repositories. It essentially works by providing a way to check out multiple projects (Git repositories) in parallel based on a manifest file (which basically serves the purpose that a parent repository does for Git submodules – tracking which submodule commits go together). It also provides a way to submit an atomic changeset that includes changes to multiple different projects.
The downside is that Repo doesn’t handle merging very well: it essentially expects you to rebase your changes when you want to bring in outside updates, effectively bringing things back to the equivalent of svn update. If you’re a fan of many small commits over a few large ones, this can get onerous.
Gitslave
Gitslave is a wrapper around Git that multiplexes git commits into multiple repositories. It effectively implements the “have parallel branches for each of your projects” solution to the merging problem by doing that for you – if you create a branch, it gets created everywhere. If you commit, all of your repositories create a commit, and so on.
Of course, this can get rather hectic if you have a large number of projects and start running into things like merge conflicts in 5 different repositories. It also means you potentially wind up making a lot of pointless extra branches in projects that you didn’t happen to touch while touching another project.
Git Subtree
Git Subtree is a tool that uses Git’s “subtree merge” functionality to get a similar result to submodules, but via actually storing the files in the main repository and merging in changes directly to that repository.
The upside is that you avoid all the issues with submodule merging because the contents of your subprojects are stored directly in the parent repository and thus are treated like any other tracked files when pulling and merging.
The downside is that all of your subproject files are present in the parent repository, which means you’re giving up some of the reason for originally splitting up your project repositories: having one canonical repository for a given set of shared code. If someone makes a change to a subproject, they can merge it with other changes locally, but they’d have to explicitly split that change back out of their project if they wanted to share it with projects.
untracked content
If something new is created in submodules, git status
in parent repo will show
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)
(commit or discard the untracked or modified content in submodules)
modified: Channel_ANS_test (untracked content)
If the content in submodules has been committed, the status shows that
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: Channel_ANS_test (new commits)
Submodules changed but not updated:
* Channel_ANS_test 480c2e3...78636ff (1):
> add gitignore for n.cc and codegenProtos.hh
no changes added to commit (use "git add" and/or "git commit -a")
Workflow for integration
The workflow for integrating a package is as follows:
- The official code repository is in the github (known as official tree).
- Each team has a corresponding repository in the github (known as team repository)
- The code to integrate should be in the local git repository
- The code has to be pushed to the team repository in the github and a pull request has to be opened to integrator.
- Jenkins server is then activated:
- Pull the code
- Compile it.
- Perform sanity tests
- Integrator then pull the new code and makes its own checks: functionality, bugs correction, integration
- Issues are opened and tracked via trac server
- If the new code is good enough, it is pushed to the official tree.
Not afraid of “git rebase”
git rebase
in its simplest form is a command which will merge another branch into the branch where you are currently working, and move all of the local commits that are ahead of the rebased branch to the top of the history on that branch. So it avoid merge conflict.
git rebase origin/master # remote
git rebase dev # local
If there are conflicts, and you can use git rebase --continue
after fixing them. If the conflicts are too bad and you need to bail out and attempt a normal fast-forward merge, you can easily do so with git rebase --abort
(leaving you where you were before attempting the rebase). Do not forget to use git log --oneline
to check the result sometimes.
Not to mention that interactive rebase
is fantastically useful.
git rebase -i HEAD~n # n means showing first three commits
# -i means interative
-
Quickly removing a commit which snuck into a different branch.
In interative mode, just delete the line:pick <commitID> <commitDescription>
, the same value withgit reset --hard <commidID>
, but different essence.
And make sure that you have commited at least 3 times before, if not, it will do nothing. -
Squashing several commits into one.
In interative mode, just revise key wordpick
to squash, save and quit. -
Rapidly changing the message on a series of commits.
In interative mode, just revise key wordpick
to edit, save and quit. At the moment, you have entered editable commit mode:
According to git reminds, usegit commit --amend
to modify commit messages. If you are satified with your edit, typegit rebase --continue
. Save and quit.
Duplicating a repositoty via mirroring a repository
The following method exhibits how to duplicate a remote repository and do not update with old repo.
-
Create a bare clone of the repository. To be honest, it will download all your project. Because .git dir has it all.
git clone --bare https://github.com/exampleuser/old-repository.git
- Mirror-push to the new repository. It will push all local things to remote.
cd old-repository.git git push --mirror https://github.com/exampleuser/new-repository.git
- Remove the temporary local repository you created in step 1.
cd ..
rm -rf old-repository.git
git diff
Use git diff
to check the difference of branches.
cd dir
for f in *n.hh; do
git diff brnach1 branch2 -- $f >> ../branch12.txt
done
Using Git submodules with Gitlab CI
- Move submodules and super project to the same group, this is called transfer project, you can find it in setting-advance setting. Before transfering project, you should build a group or use existing group. If you add new project to a group, you can click group and click New Project next. For existing project,
transfer project
means a new namespace and you should update their remote url of local projects withgit remote set-url origin <new ssh or http url>
. - Of course, starting with GitLab 10.3, existing Git remote URLs for projects under the namespace will redirect to the new remote URL. Every time you push/pull to a repository that has changed its location, a warning message to update your remote will be displayed instead of rejecting your action. This means that any automation scripts, or Git clients will continue to work after a rename, making any transition a lot smoother.
- Make sure your super project and subprojects are on the same GitLab server. If subproject and superproject are the same level of a group, use relative path in
.gitmodules
: modify your.gitmodules
file to[submodule "project"] path = project url = ../../group/project.git
Git Basics - Tagging
Git supports two types of tags: lightweight and annotated. A lightweight tag is very much like a branch that doesn’t change — it’s just a pointer to a specific commit. Annotated tags, however, are stored as full objects in the Git database. They’re checksummed; contain the tagger name, email, and date; have a tagging message; and can be signed and verified with GNU Privacy Guard (GPG). You can use git show <tag-name>
to recognise which one it belongs to.
git tag %show tag list
git tag -a v1.4 -m "my version 1.4" %create an annotated tag
git tag v1.4-lw %create a lightweight tag
git tag -a v1.2 <commit-id> %create tag later
git large file
Install git-lfs
curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo zsh
sudo apt-get install git-lfs
git lfs install
If you don’t wanna install lfs globally, you can use git lfs uninstall
to delete it and use git lfs install --local
instead to make it usable in the repository.
Name explaination
origin vs master
Just like the branch name “master” does not have any special meaning in Git, neither does “origin”. While “master” is the default name for a starting branch when you run git init
which is the only reason it’s widely used, “origin” is the default name for a remote when you run git clone
. If you run git clone -o booyah
instead, then you will have booyah/master as your default remote branch.
For better understanding, you can see the origin and master changing with these commands.
This is the primitive condition.
Someone pushes some commit to remote(origin), I(My computer) commit something locally. As you can see, my origin/master pointer doesn’t move.
I use command git fetch origin
to my project locally. The origin/master header pointer has been moved.
Others add another origin called teamone remotely.
I use command ‘git fetch teamone` to my project locally. Because that server has a subset of the data your origin server has right now, Git fetches no data but sets a remote-tracking branch called teamone/master to point to the commit that teamone has as its master branch.
Tracking Branch
Checking out a local branch from a remote-tracking branch automatically creates what is called a “tracking branch” (and the branch it tracks is called an “upstream branch”).
Tracking branches are local branches that have a direct relationship to a remote branch. If you’re on a tracking branch and type git pull, Git automatically knows which server to fetch from and which branch to merge in.