I recently set up an intranet server in our company, and we will use Git for version control in future projects. Here are some commonly used operations for reference.
Install Git
Windows: You can download the Git installer/portable version from the official website. The difference between the installer and the portable version is that the portable version requires you to manually add GitBinaryPath/bin to the system’s Path.
Linux (debian): sudo apt-get install git
Initial Setup
After installation, start git.exe (Windows) or enter in the terminal on Linux:
1 | $ git config --global user.name "Your Name" |
Generate SSH Key (replace the email address with your own):
1 | $ ssh-keygen -t rsa -C "youremail@example.com" |
Then press Enter multiple times (or set your own password). After completion, a .ssh
directory will be created under C:\Users\${userName}\
(in Linux, it will be under ~/), containing two files: id_rsa
and id_rsa.pub
.
id_rsa
is the private key and should not be disclosed, while id_rsa.pub
is the public key, which you can safely share with anyone.
Next, add your SSH Key to your account on the remote Git platform (GitHub, GitLab).
This is necessary because GitHub needs to verify that the commits you push are indeed from you and not someone impersonating you. Git supports the SSH protocol, so once GitHub knows your public key, it can confirm that only you can push.
Of course, GitHub allows you to add multiple keys. Suppose you have several computers, and you submit from the company sometimes and from home at other times. Just add the key from each computer to GitHub, and you can push from each computer.
Create Local Repository
Start Git Bash and then cd
(or use Easy Context Menu to add GitBash to the system’s right-click menu) to the directory where you want to create the local repository and execute the following command:
1 | // Initialize a directory |
Local Commit Files
1 | $ git add ${file_name} |
Add Remote Repository
1 | // Add a remote repository (you can customize ${repo_name}) |
Remove Remote Repository
1 | // If you want to remove the remote repository associated with the local repository, you can execute the command below |
Push to Remote Repository
1 | // ${branch} defaults to master (main branch) but can be modified (not recommended). -f forces submission while ignoring conflicts. |
Push to Multiple Remote Repositories
There are two ways to push to two or more remote repositories at once.
The first method is to use Git’s set-url --add
command:
1 | # Add the first remote repository |
The second method involves directly editing the .git/config file to add multiple remote repository addresses under the same remote name:
1 | $ nano .git/config |
Create New Branch
1 | // -b means base, which creates a new branch based on the current branch |
checkout
has three uses: switching branches, discarding changes, and checking out from historical versions; these will be discussed later.
1 | # Create a local branch based on the remote origin/level |
Push Current Local Branch to Remote Branch
1 | // If the ${new_branch_name} branch does not exist remotely, it will be created automatically |
Merge Branches
1 | // First, switch back to the branch to merge into, such as merging the dev branch into the master branch |
Cancel Merge
If there are conflicts between the local and remote branches (or local branches), you can cancel the merge to revert the local branch to its prior state.
1 | $ git merge --abort |
Resolve Conflicts
In Git, if there’s a conflict in text files, you can directly see the diffs during the pull, but if Git is managing binary files, it’s not as straightforward.
If you want to revert a local commit ${123456}
, you can use a reset to an earlier version, and then use:
1 | $ git reset --hard 123455 |
Binary File Conflicts
In Git, you can see merges in text files, but binary file merges can be troublesome.
If we want to keep the remote version of git.exe
, we can execute the following command:
1 | # Keep the remote version of git.exe |
To keep the local version of the file, the command is:
1 | $ git checkout --ours git.exe |
Then execute:
1 | $ git add git.exe |
Now your local copy is merged with the remote, and the merge maintains the remote file version.
Delete Branches
1 | // Delete the local branch |
Undo Deleted Branches
Two scenarios
- Exited Terminal
Use git reflog
to check your last commit SHA1
value
1 | $ git branch ${branch_name} ${SHA1} |
You can create a branch according to your SHA1
value, using the commit
you can choose the commit’s SHA1
from when the branch was deleted.
- Not Exited Terminal
When deleting a branch, the SHA1
value will be available.
1 | $ git branch -d dev |
Then use this SHA1
value to recover the deleted branch
1 | $ git branch ${branch_name} ffe7c46 |
Version Rollback
1 | // View all committed versions |
1 | // Rollback to a certain version |
1 | // Rollback a file to a certain version |
Undo Uncommitted File Modifications
Undo Only Local Modifications
Suppose we modify some files within a branch but have not moved them to the staging area
(git add).
After modifying the files, use the status
command to check the file status
Git prompts us that for files not added to the staging area, we can use git checkout --
to quickly revert local modifications.
1 | // Quickly undo local modifications |
Undo Modifications in Both Local and Staging Area
For files that have been added to the staging area, how can we revert local modifications? Use the status
command to check file status again:
We can first unstage:
1 | $ git reset ${filename} |
Then undo local modifications:
1 | // Quickly undo local modifications |
One-command Quick Undo Modifications
1 | $ git checkout HEAD -- ${filename} |
Check Out Files from History
Sometimes, after submitting a new version, certain file changes may feel inappropriate, and you’d like to revert a few files to a previous version while keeping others in the new version. You can use the checkout
command.
1 | // View committed historical versions |
Subsequently, you can use add
to commit as usual.
Rename Local Branch
1 | git branch -m ${CurrentBranchName} ${NewCurrentBranchName} |
Clone Remote Repository
1 | // Clone all branches of the repository |
Switch to Remote Branch
After cloning a remote repository, executing git branch
will show only:
1 | * master |
No other branches will be visible, even if there are other branches in the remote repository.
You can use git branch -va
to first list all local/remote branches:
1 | $ git branch -va |
Switch to the remote origin/master
branch:
1 | $ git checkout remotes/origin/master |
Further operations are needed:
1 | $ git checkout -b ${remote_branch_to_local_branch_name} |
-b
means base; it creates a new branch named dev based on the current branch. Other naming can be used as well. Now, executing git branch -va
will show:
And that’s it!
After modifying the dev branch locally, you can directly push it to the remote dev branch:
1 | $ git push origin dev |
Clone a Specific Branch from Remote Repository
1 | $ git clone -b ${OriginRepoBranchName} ${OriginRepoAddr} |
For example:
1 | git clone -b master git@github.com:imzlp/blog-source.git --depth=2 |
This pulls the master
branch from the remote repository, with --depth
specifying the history version pull depth.
Ignore Unnecessary Submitted Files
Sometimes we do not want to submit everything to the repository (e.g., some compiled binary files). We can use .gitignore
to exclude files that should not be submitted.
In Windows, we cannot directly create this file because it starts with a dot and has no filename, so we need to use a command in GitBash to create it:
1 | $ touch .gitignore |
Then edit .gitignore
to add the files/directories you wish to exclude.
1 | # Exclude the public folder in the current directory |
For more content on .gitignore
, you can check here — Ignoring Specific Files - Git Tutorial
Compare Differences between Different Versions
1 | # View what portions of unstaged files have been updated |
Diff between Two Branches
A common diff usage is to compare the workspace, index, HEAD, or the diff between two commits. Besides these uses, diff can also compare two branches, as follows:
1 | $ git diff topic master (1) |
Usage 1: Directly separating two branches with a space will diff the latest commits from both branches, equivalent to diffing two commits.
Usage 2: Splitting branch names with two dot characters has the same effect as usage 1.
Usage 3: Splitting branch names with three dot characters outputs the changes on the master branch since the topic branch was last developed.
Note that here ..
and ...
should not be confused with ..
and ...
in git rev-list
.
Error Handling
bad signature
If you encounter the following error while using the git
terminal:
1 | $ git status |
This is caused by a corrupted index, which can be handled as follows:
1 | rm -f .git/index |
For more details, refer to the discussion on stack overflow about: How to resolve “Error: bad index – Fatal: index file corrupt” when using Git
Disable Git’s SSL Verification
1 | $ git config --global http.sslVerify false |
Private Key Permissions Too Open Error
When using Git, you may sometimes encounter such prompts (usually when sharing private keys between machines).
1 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
This prompts that your Git private key has overly open read/write permissions (I have it set to 644), and the solution is to change the permissions of the private key (id_rsa) to 600
. You can check Tools, Environment Knowledge Collection: Linux File Permissions Modification for Linux read/write permissions.
1 | sudo chmod 600 ~/.ssh/id_rsa |
For Windows, you can right-click on the file .ssh/id_rsa
, go to Properties - Security/Advanced
, disable inheritance, change the file owner to your account, and ensure that your account and SYSTEM
have Read and Execute
permissions, then save.
Alternatively, you can use WSL to set permissions using
chmod
.
Reference article: Windows SSH: Permissions for ‘private-key’ are too open
Update Remote Branch List in Git
1 | $ git remote update origin --prune |
Submodule
If you want to use another Git repository within one Git repository, you can handle this relationship using git module
.
If you clone another repository B into a subdirectory of Git repository A, you may see a message like:
1 | hint: You've added another git repository inside your current repository. |
The purpose is to inform you that you have included another Git repository.
The correct way to handle this is to first delete the previously cloned B, then clone B using git submodule
:
1 | $ git submodule add git@github.com:ufna/VaRest.git Plugins/VaRest |
It will clone B, then you’ll go into B’s directory, change the version record to the desired commit, and then add/commit as usual.
1 | $ git status |
Then push to the remote repository, and in the remote repository, the B directory in project A will not be directly uploaded files but will link to the actual source repository of B.
Initial Update of Submodule
1 | $ git submodule update --init --recursive |
Subsequent Updates to Submodule
1 | $ git submodule foreach git fetch |
Remove Submodule
To remove an added submodule, you need to execute the following steps:
- Delete the submodule directory.
- Remove the related information about this submodule from
.gitmodules
. - Remove the related information about this submodule from
.git/config
. - Delete the submodule directory from
.git/modules
.
After completing the above steps, that should be enough. However, if there are errors, you may remove the cache:
1 | $ git rm --cached submodule_name |
Update All Submodules
1 | git submodule foreach git pull origin master |
git gc
If you see a prompt like this during a pull:
1 | Auto packing the repository for optimum performance. You may also |
This is because Git uses a format called loose objects to save objects to disk by default. Occasionally, Git packages these objects into a binary file called a packfile to save space and improve efficiency. When there are too many loose objects in the repository, it prompts you to run git gc
.
Thus, when you see the above message, simply run:
1 | $ git gc |
Once completed, you’re good to go.
Pull Branch from Remote
1 | # Check Remote Branches |
Method one:
1 | # Pull a remote branch and create a local branch |
Method two:
1 | $ git fetch origin ${RemoteBranchName}:${LocalBranchName} |
fatal: The remote end hung up unexpectedly
You can increase postBuffer
to a larger size (in bytes):
1 | $ git config http.postBuffer 524288000 |
Enable Case Sensitivity in Git
Git on Windows defaults to being case-insensitive. You can enable it through configuration:
1 | $ git config core.ignorecase false |
Git Proxy Settings
1 | git config --global http.proxy 'socks5://127.0.0.1:1080' |
Unset git proxy:
1 | git config --global --unset http.proxy |
Create a Local Empty Repository
1 | $ git --bare init |
This command creates an empty repository, which can be added as a remote repository in other projects.
Early EOF Error
1 | user@USER ~ |
Solution: Disable core.compression
:
1 | git config --global core.compression 0 |
Repository Not Found
If a pull indicates:
1 | $ git.exe pull --progress -v --no-rebase "origin" |
After ensuring that the server connection is normal and SSH Key is fine, you can resolve this with the methods below.
The issue may arise because another Git account was previously used on this computer, and passwords were saved in the computer settings. When requesting the remote repository, the saved password account will be used by default, and if the request account lacks access to the repository, this error will occur.
Thus, the solution is to delete unnecessary credentials in the control panel:
After this, relaunch the Clone or pull, and you will be prompted to enter the git account and password again.
Export Diff Files
Sometimes, we need to know which files have been updated between two versions without wanting to see the specific changes. You can use the git diff
command as follows:
1 | $ git diff COMMIT_HASH_1...COMMENT_HASH_2 --name-only > diff.txt |
This will list all modified file names, with the key point being the --name-only
parameter.
PS:
COMMIT_HASH_1...COMMENT_HASH_2
must have the older version first and the newer version second.
List Commit History
Unlike directly using git log
, I want a simple list of each commit record without caring about the committer, time, and so on.
You can use Git’s placeholders: Git Basics - Viewing the Commit History
1 | $ git log --pretty=format:"%h \"%s\"" |
The --pretty=format:
supports more options:
Option | Description of Output |
---|---|
%H |
Commit hash |
%h |
Abbreviated commit hash |
%T |
Tree hash |
%t |
Abbreviated tree hash |
%P |
Parent hashes |
%p |
Abbreviated parent hashes |
%an |
Author name |
%ae |
Author email |
%ad |
Author date (format respects the –date=option) |
%ar |
Author date, relative |
%cn |
Committer name |
%ce |
Committer email |
%cd |
Committer date |
%cr |
Committer date, relative |
%s |
Subject |
Rebase
If you only use merge
to combine branches, you will merge all commit information and submission history from the target branch. Sometimes we want to treat a version of features as a single commit rather than treating every small modification as one commit. This is how we can use rebase.
First, here’s the flow of rebase:
- Create Branch B based on Branch A:
git checkout -b B
- Make modifications and commits on Branch B until functionality is satisfactory.
- When you want to sync Branch B with Branch A, you can use rebase to fold commit:
Specifically, find the foundational commit hash from Branch A in Branch B and run the following command:
1 | # Specify the base version for rebase |
This will open a vim editor interface with all commits from Branch B listed from the base version to HEAD, all defaulting to pick. Since we need to fold commits, we should merge multiple commits, thus using the squash
command provided by git:
- pick: Keep the commit (abbreviation: p)
- reword: Keep the commit but I want to modify the commit note (abbreviation: r)
- edit: Keep the commit but I want to stop to modify (not just the note) (abbreviation: e)
- squash: Merge the commit with the previous one (abbreviation: s)
- fixup: Merge the commit with the previous one but do not keep the commit note (abbreviation: f)
- exec: Execute shell command (abbreviation: x)
- drop: Discard the commit (abbreviation: d)
Only keep one pick, replacing others with squash should suffice. After that, another commit editing interface will pop up, allowing you to comment out any unwanted commit messages with #
, then save and exit, completing the rebase.
- The last step is to switch back to branch A and execute the merge operation:
git merge B
. After merging, you will only see a single commit in branch A.
Clean LFS Cache
When using LFS for a while, the cache can become excessively large. You can use the following command to clean it:
1 | git lfs prune |
error: cannot lock ref
1 | error: cannot lock ref 'refs/remotes/origin/SN/feature/Terrain_CDLod': is at 076e430ca921e6a02b6c2a431609090e635f8ea8 but expected 48b8b9ebd04ab04a7053d3aca304beca46da16f6 |
Solution:
1 | $ git update-ref -d refs/remotes/origin/SN/feature/Terrain_CDLod |
View Files Pending Commit in Git
git diff
AMCR captures four types of operations: Add/Modify/Copy/Rename:
1 | git diff --cached --name-only --diff-filter=AMCR |
Using -cached
detects differences since the last commit after git add
.
1 | $ git diff --cached --name-only --diff-filter=AMCRX |
You can view the specific parameters for Git: –diff-filter.
git status
Using git diff
can only compare between versions, or files after git add
, but cannot access untracked files.
1 | git status -s |
This will show a short list:
1 | M Assets/Scene/BaseMaterials/4X4_MRA.uasset |
The first two characters indicate the file status, followed by a space. ??
represents untracked files. More status codes can be found in git-status.
Revert Commit
1 | git reset --soft ^HEAD |
Delete Tag on GitHub
1 | git push origin --delete tagname |
Commit with Username and Password
1 | git remote set-url origin https://username:password@github.com/username/repo_name.git |
Be sure to modify the LFS as well, and you can edit the .git/config
file.
SSH Login with Key Failure
You might edit Mac’s SSH configuration (/etc/ssh/sshd_config
):
1 | RSAAuthentication yes |
Then reload the configuration:
1 | $ sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist |
error: cannot lock ref
1 | error: cannot lock ref 'refs/remotes/origin/SN/feature/avinchen/Develop': is at 651a8bcf620db8e6bbc6c874e0949ff6c09bb37f but expected f0f49ccce3abfe552e2301eca5711741a390af6b |
Solution:
1 | $ git update-ref -d refs/remotes/origin/SN/feature/avinchen/Develop |
Alternatively, you could force pull all:
1 | $ git pull -p |
Pull LFS Files
1 | git lfs fetch |
Pull Request to Local
1 | git fetch origin pull/58/head:ue5 |
This means pulling the 58th remote PR to the local ue5 branch.
Historical Files Support LFS
After enabling LFS, you can track files from historical commits as well.
1 | # Using the current gitattributes configuration |
Remove Changed Files
To remove all changed files:
1 | git clean -xdf |
Note: This will delete all untracked files (including those ignored in gitignore
). If you do not wish to delete files ignored in gitignore
, omit the x
in the command above.
no hostkey alg
When connecting to the SSH Server, if this prompt appears, it is due to the SSH version, as the remote SSHD version is too high while the local SSH version is too low.
1 | OpenSSH_5.1p1, OpenSSL 0.9.8i 15 Sep 2008 |
UE updated the client’s SSH version in the 4.27 engine: Replace Windows ssh and rsync from DeltaCopy with new version from cw…
If it is an engine version prior to 4.27, upgrading to MacOS13+ will lead to this issue during remote builds. ## detected dubious ownership in repository at
1 | $ git remote set-url origin https://git.woa.com/xxxx/UnrealEngine.git |
If you’ve switched to a new account, the above error will appear for repositories pulled using the previous account.
The solution is to add all paths to the safe directory:
1 | git config --global --add safe.directory "*" |
Note: This command should be executed in the new account.
Warning: the ECDSA host key
When you encounter this warning while committing to GitHub: Warning: the ECDSA host key for 'github.com' differs from the key for the IP address '192.30.255.112'
, it means your SSH client has previously authenticated a different host matching the target IP address.
To resolve this issue, follow these steps:
1. Remove the old key from the known hosts file:
Open the ~/.ssh/known_hosts
file with a text editor (Windows users should locate it in %UserProfile%\.ssh\known_hosts
) and find the corresponding entry (referencing the IP address and hostname), then delete the corresponding line.
For example, in the known_hosts
file, find an entry like:
1 | github.com,192.30.255.112 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hX ... |
Delete that line.
2. Verify GitHub’s SSH host key fingerprint:
Confirm the current valid TLS public key fingerprint and SSH public key fingerprint from GitHub’s official documentation.
3. Reconnect via SSH and add the new host key:
In the terminal or command prompt, run the following command:
1 | ssh -T -oStrictHostKeyChecking=accept-new git@github.com |
This will automatically add the new github.com
host key.
If prompted “Hi username! You’ve successfully authenticated, but GitHub does not provide shell access.”, it means SSH authentication was successful and the issue is resolved.
Now, you should be able to commit code to GitHub without the warning message reappearing.
pull/push prompts for password every time
You can execute the following command:
1 | git config --global credential.helper store |
Get the latest commit information of the current repository
If you only want to get the latest commit ID, you can use the following command:
1 | $ git rev-parse HEAD |
If you want to see the branch and commit information, you can use the command below:
1 | $ git log -1 --pretty=oneline |
The -1
parameter lists only the latest commit.
If you add --decorate
, you can see the branch information (depending on the Git version, newer versions may show branch information by default):
1 | $ git log -1 --pretty=oneline --decorate |
SSH key login failed
If the SSH connection prompts the following error:
1 | lipengzha@192.168.31.55: Permission denied (publickey,password,keyboard-interactive). |
You can modify the server-side SSH configuration (/etc/ssh/sshd_config
):
1 | RSAAuthentication yes |
Then restart the SSHD service.
View the last modifier of a particular line of code
To view the last committer of code from line n~m
of a file:
1 | git blame filename -L n,m |
Will output:
1 | e74836f557b (lipengzha 2024-01-10 09:43:02 +0800 541) TArray<FString> FoundShaderLibs = UFlibShaderCodeLibraryHelper::FindCookedShaderLibByPlatform(PlatformName,SavePath,false); |
1 | git blame -L 541,541 --incremental --minimal Plugins\\HotPatcher\\HotPatcher\\Source\\HotPatcherCore\\Private\\CreatePatch\\PatcherProxy.cpp |
Output:
1 | e74836f557bc5ddf01de3f5f8d3b797107d9a767 541 541 1 |
Clear all commit history
To clear all history in a Git repository while keeping only the latest commit, follow these steps:
Create a new branch:
First, ensure you are in a clean working directory (no uncommitted changes) and create a new branch.1
git checkout --orphan latest_branch
This command creates a new branch without any history.
Add all files:
Add all files to the new branch.1
git add -A
Commit the changes:
Commit all files.1
git commit -am "Initial commit with latest state"
Delete the old branch:
Switch back to the main branch and delete the old branch.1
git branch -D main
Rename the new branch to the main branch:
Rename the new branch to the main branch.1
git branch -m main
Force push to the remote repository:
Finally, force push the changes to the remote repository. Note: This will overwrite all history in the remote repository.1
git push -f origin main
This operation will clear all historical records, keeping only the latest commit. This operation is irreversible, and all historical records will be permanently deleted, so ensure you truly do not need these historical records before executing this operation.
Differences in line endings between Win and Linux
Windows uses carriage return line feed (CRLF, \r\n
), while Linux uses line feed (LF, \n
). If you create or modify files on Windows and then view them on Linux, Git may think the files have changed due to the different line endings.
Solution: You can configure Git to handle line endings. Use the following commands to set Git to automatically convert line endings:
1 | git config --global core.autocrlf true # Windows |
- Use the
.gitattributes
file to specify the handling of line endings for specific files, for example:
1 | * text=auto |
UPDATE
CheatSheet
References
Detailed explanation of Git remote operations
Creating and merging branches
Undoing branch deletion in Git
Using git checkout command to undo modifications
Diffing two branches with git diff