I recently set up an intranet server, and from now on, all projects will use Git for version control. Here, I’ve outlined some common operations for easy reference.
Install Git
Windows: You can download the Git installation package/portable version from the official website. The difference between the installation package and the portable version is that the portable version requires you to manually add GitBinaryPath/bin to the system Path.
Linux (debian): sudo apt-get install git
Initial Setup
After installation, start git.exe (Windows) or just type in the terminal:
1 | $ git config --global user.name "Your Name" |
Generate SSH Key (replace with your own email address):
1 | $ ssh-keygen -t rsa -C "youremail@example.com" |
Press Enter all the way (or set a password if you prefer), and once done, a .ssh
directory will be created in C:\Users\${userName}\
(on Linux, it will be in ~/) containing two files: id_rsa
and id_rsa.pub
.
id_rsa
is the private key, which must not be leaked; id_rsa.pub
is the public key, which you can share with anyone.
Then, add your SSH Key to your account on the remote Git platform (GitHub, GitLab).
GitHub needs to verify that the commits you push are indeed from you, not someone impersonating you. Since Git supports SSH protocol, GitHub can confirm that only you can push as long as it knows your public key.
Of course, GitHub allows you to add multiple keys. Suppose you have several computers – you can push from your work computer or your home computer; just add each computer’s key to GitHub, and you can push from any of them.
Create a Local Repository
Start Git Bash, then cd (or use Easy Context Menu to add Git Bash 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 (${repo_name} can be customized) |
Delete Remote Repository
1 | // To remove the association of the local repository with a remote repository, you can execute the following command |
Push to Remote Repository
1 | // ${branch} defaults to master (the main branch) but can also be modified (not recommended). -f means to ignore conflicts and force the push. |
Push to Multiple Remote Repositories
There are two ways to push to two or more remote repositories.
The first method is to use the git set-url --add
command:
1 | # Add the first remote repository |
The second method is to directly edit the .git/config file to add multiple remote repository addresses to the same remote repository name:
1 | $ nano .git/config |
Create Branch
1 | // -b means base, which creates a new branch based on the current branch |
The checkout
command has three uses: switch branches, discard changes, and check out from history, which will be explained later.
1 | # Create a local branch based on the remote origin/level |
Push Local Current Branch to Remote Branch
1 | // If the ${new_branch_name} branch does not exist on the remote, it will be created automatically |
Merge Branches
1 | // First switch back to the branch you want to merge into, for example, merging the dev branch into the master branch |
Abort Merge
If there are conflicts between the local and remote branches (or local branches), you can abort the merge to revert the local branch to its state before the merge.
1 | $ git merge --abort |
Resolve Conflicts
In Git, if there are conflicts in text files, you can directly see the differences while pulling, but if Git is managing binary files, it is less convenient.
If you want to undo the changes of a specific commit ${123456}
, you can revert to the previous version and then use:
1 | $ git reset --hard 123455 |
Binary File Conflicts
Text file merges in Git can be easily viewed, but binary files can be cumbersome.
If we want to keep the remote branch version of git.exe
, we can execute the following command:
1 | # Keep the remote version of git.exe |
And to keep the local branch file:
1 | $ git checkout --ours git.exe |
Then execute:
1 | $ git add git.exe |
At this point, the local branch is merged with the remote while retaining the remote file version.
Delete Branch
1 | // Delete local branch |
Undo Deleted Branch
Two scenarios:
- If you have exited the Terminal
Run git reflog
to see your last commit SHA1
1 | $ git branch ${branch_name} ${SHA1} |
You can recreate a branch based on your SHA1
, choosing the commit SHA1
of the delete operation.
- If you have not exited the Terminal
When you delete a branch, you’ll have the SHA1
value
1 | $ git branch -d dev |
Then use that SHA1
value to restore the deleted branch:
1 | $ git branch ${branch_name} ffe7c46 |
Version Rollback
1 | // View all submitted versions |
1 | // Roll back to a specific version |
1 | // Roll back a specific file to a certain version |
Undo Uncommitted File Changes
Only Undo Local Changes
If you have made modifications to some files in a branch but haven’t added them to the staging area (git add), check the file status using the status
command.
Git suggests that for files not added to the staging area, you can use git checkout --
to quickly undo local modifications.
1 | // Quickly undo local modifications |
Undo Both Local and Staged Modifications
For files already added to the staging area, how do you undo local changes? First, check the file status:
First, unstage:
1 | $ git reset ${filename} |
Then undo local changes:
1 | // Quickly undo local modifications |
Quick Undo Changes All at Once
1 | $ git checkout HEAD -- ${filename} |
Check Out Files from History
Sometimes you may commit a new version but feel that changes to certain files are inappropriate and want to revert some files to a previous version while keeping others at the new version. You can use the checkout
command.
1 | // View commit history |
Later, you can use add
to commit just like ordinary files.
Rename Local Branch
1 | git branch -m ${CurrentBranchName} ${NewCurrentBranchName} |
Clone Remote Repository
1 | // Clone the entire repository |
Switch to Remote Branch
Once a remote repository is cloned with git clone
, executing git branch
will only show:
1 | * master |
Other branches will not be visible even if there are more in the remote repository.
You can first list local/remote branches with git branch -va
:
1 | $ git branch -va |
To switch to the remote origin/master
branch:
1 | $ git checkout remotes/origin/master |
Further operations are required:
1 | $ git checkout -b ${remote_branch_to_local_branch_name} |
-b
means base
, creating a new branch named dev
based on the current branch. You can certainly use other names here. Once again executing git branch -va
will show:
And you’re done~
After making changes locally in the dev
branch, you can directly push to the remote dev
branch:
1 | $ git push origin dev |
Clone 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, with --depth
specifying the historical version’s depth to retrieve.
Ignore Unnecessary Files for Commit
Sometimes we don’t want to commit everything to the repository (like some compiled binary files); we can use .gitignore
to exclude files that should not be committed. On Windows, you cannot create this file directly because it starts with a dot without a filename, so use the command in Git Bash to create it:
1 | $ touch .gitignore |
Then edit .gitignore
and add the files/directories you want to exclude.
1 | # Exclude the public folder in the current directory |
For more information about .gitignore
, you can check here — Ignoring Special Files - Git Tutorial
Compare Differences Between Versions
1 | # View which parts of untracked files have been updated |
Diff Between Two Branches
A common use of diff is to compare the working directory, index, HEAD, or the diff between two commits. In addition to these uses, diff can also compare two branches as follows:
1 | $ git diff topic master (1) |
Usage 1: Directly comparing two branches separated by a space will perform a diff on the latest commits of the two branches, equivalent to diffing two commits.
Usage 2: Using two dots between the branch names has the same effect as usage 1 (just looking cool).
Usage 3: Using three dots between the branch names will output the changes on master since topic was first developed.
Note that the ..
and ...
here should not be confused with those 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 index corruption, which can be handled as follows:
1 | rm -f .git/index |
For more details, refer to the discussion on stack overflow here: How to resolve “Error: bad index – Fatal: index file corrupt” when using Git
Disable SSL Verification in Git
1 | $ git config --global http.sslVerify false |
Private Key Permission Too Open Error
Sometimes, when using Git, you might see this prompt (usually when sharing keys across machines).
1 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
This warns that your Git private key’s read/write permissions are too open and pose a security risk (in my case, it’s 644), and the solution is to change the private key (id_rsa) permissions to 600
. For Linux read/write permissions, check Knowledge Accumulation of Tools and Environment: Linux File Permission Modification.
1 | sudo chmod 600 ~/.ssh/id_rsa |
For Windows, the solution is:
Right-click on the .ssh/id_rsa
file, go to properties - Security / Advanced
, disable inheritance, change the file owner to your own account, and add your own account and SYSTEM
with Read and Execute
permissions, then save.
Alternatively, use WSL to set permissions with
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 use another Git repository within a Git repository, you can handle this inclusion with git module
.
If you directly clone another repository B into subdirectory A, there will be similar prompts when you git add
:
1 | hint: You've added another git repository inside your current repository. |
This is to inform you that you have included another Git repository.
The proper way to handle this is to delete the previously cloned B and then use git submodule
to clone B:
1 | $ git submodule add git@github.com:ufna/VaRest.git Plugins/VaRest |
This will clone B, then go into B’s directory, change the version record to your desired commit, and then add/commit it.
1 | $ git status |
Then push to the remote repository. In the remote repository, the B directory in project A will not contain directly uploaded files but will link to the actual source repository of B.
Initial Update Submodule
1 | $ git submodule update --init --recursive |
Subsequent Updates to Submodule
1 | $ git submodule foreach git fetch |
Deleting Submodule
To delete an added submodule, follow these steps:
- Delete the submodule’s directory.
- Remove the related submodule info from
.gitmodules
. - Remove the related submodule info from
.git/config
. - Delete the submodule’s directory from
.git/modules
.
After performing the above steps, you are good to go. If there are error issues, you can 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 the following prompt when pulling:
1 | Auto packing the repository for optimum performance. You may also |
Git uses a format called loose objects
when saving objects on disk by default. From time to time, Git packs 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, you will be prompted to run git gc
.
So, when you see the above prompt, just run in the terminal:
1 | $ git gc |
Wait for it to finish.
Pull Branch from Remote
1 | # View remote branches |
Method 1:
1 | # Pull remote branch and create local branch |
Method 2:
1 | $ git fetch origin ${RemoteBranchName}:${LocalBranchName} |
Fatal: The Remote End Hung Up Unexpectedly
Increasing postBuffer might help (in bytes):
1 | $ git config http.postBuffer 524288000 |
Enable Case Sensitivity in Git
Git is case insensitive by default on Windows. You can enable it by modifying the configuration:
1 | $ git config core.ignorecase false |
Set Git Proxy
1 | git config --global http.proxy 'socks5://127.0.0.1:1080' |
To cancel git proxy:
1 | git config --global --unset http.proxy |
Create Empty Local Repository
1 | $ git --bare init |
This command creates an empty repository that 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 you encounter the following when pulling:
1 | $ git.exe pull--progress-v--no-rebase "origin" |
After confirming that the server connectivity is normal and the SSH Key has no issues, you can solve it using the following method.
The reason for this issue is that another Git account has previously used this computer, and passwords were saved. When requesting the remote repository, it defaults to using the saved account, and if that account does not have access permissions, an error will occur.
The solution is to delete the extra credentials from the control panel:
After that, when you re-clone or pull, you will be prompted to enter your Git account and password.
Export Diff Files
Sometimes you need to know which files have been updated between two versions without wanting to know the detailed changes. You can use the git diff
command in this way:
1 | $ git diff COMMIT_HASH_1...COMMENT_HASH_2 --name-only > diff.txt |
This will list all changed file names, with --name-only
being the key 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 just want to simply list each commit record without caring about the author, time, etc.
You can use Git placeholders for that: 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 just use merge
to merge branches, it will carry over all commit information and submission history from the target branch. Sometimes we want a version of the feature to be treated as a single commit instead of a bunch of minor changes resulting in many commits in Git’s history. How can we solve this? By using rebase.
First, let’s describe the rebase process:
- Create branch B based on branch A:
git checkout -b B
- Make changes and commits on branch B
- When you want to synchronize finished functionality back with branch A, use rebase to condense the commits
The specific operation is to locate the commit hash from which you branched off branch A on branch B, and then use the command:
1 | # Specify the base version to rebase onto |
This will open a vim editor showing all commits from branch B since the base version to HEAD. By default, they’re all marked as pick, but since we need to condense commits, we should change multiple commits to use the squash
command:
- pick: Keep the commit (abbreviated as: p)
- reword: Keep the commit but modify the commit message (abbreviated as: r)
- edit: Keep the commit but stop to make changes to what is being committed (not just the message) (abbreviated as: e)
- squash: Merge this commit with the previous one (abbreviated as: s)
- fixup: Merge this commit with the previous one, but do not keep the commit message (abbreviated as: f)
- exec: Execute a shell command (abbreviated as: x)
- drop: Discard the commit (abbreviated as: d)
Keep only one pick and change the rest to squash. It will then pop up a commit editing interface where you can comment out the commit messages you do not need using #
, then save and exit, and the rebase is complete.
- The last step is to switch back to branch A and execute the merge operation:
git merge B
, so in branch A you will see only one commit.
Clear LFS Cache
After using LFS acceleration for a while, the cache can become excessively large, so you can use the following command to clear 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 |
The solution is:
1 | $ git update-ref -d refs/remotes/origin/SN/feature/Terrain_CDLod |
Git View Pending Commit File List
Git Diff
AMCR gets the files for: Add/Modify/Copy/Rename and other operations:
1 | git diff --cached --name-only --relative --diff-filter=AMCR |
Using --cached
checks the differences after the most recent commit considering the current git add
.
1 | $ git diff --cached --name-only --relative --diff-filter=AMCRX |
You can check the specific parameters in the git documentation: –diff-filter.
Git Status
Using git diff
can only compare versions or files after git add
, but it cannot find untracked files.
1 | git status -s |
This will show a short list:
1 | M Assets/Scene/BaseMaterials/4X4_MRA.uasset |
The first two characters denote the file status, followed by a space; ??
indicates an untracked file. You can view more status codes at git-status.
Undo Commit
1 | git reset --soft ^HEAD |
Delete Tag on GitHub
1 | git push origin --delete tagname |
Specify Username and Password during Git Commit
1 | git remote set-url origin https://username:password@github.com/username/repo_name.git |
Note that LFS also needs to be modified; you can edit the .git/config
file.
SSH Key Login Failed
You can modify 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 |
The solution:
1 | $ git update-ref -d refs/remotes/origin/SN/feature/avinchen/Develop |
Or force a full pull:
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.
History Files Supporting LFS
After enabling LFS, you can also track files within historical commits with LFS.
1 | # Use the current gitattributes configuration |
Delete Changed Files
To delete all modified files:
1 | git clean -xdf |
Note: This will delete all untracked files (including those ignored in .gitignore
). If you do not want to delete files like those in .gitignore
, remove x
from the command above.
no hostkey alg
When connecting to the SSH Server, if this prompt appears, it is due to the SSH version. 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 SSH version for the client in engine version 4.27: Replace Windows ssh and rsync from DeltaCopy with new version from cw…
If using an engine version prior to 4.27 and upgrading to MacOS 13+, this issue will occur during remote building.## detected dubious ownership in repository at
1 | $ git remote set-url origin https://git.woa.com/xxxx/UnrealEngine.git |
If you switch to a new account, for repositories pulled using the previous account, the above error message will appear.
The solution is to add all paths to the safe directory:
1 | git config --global --add safe.directory "*" |
Note: This command should be executed under 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 authenticated another host that matches the target IP address.
To resolve this issue, follow these steps:
1. Delete the old key from the known hosts file:
Open the ~/.ssh/known_hosts
file with a text editor (Windows users should find the file at %UserProfile%\.ssh\known_hosts
) and locate the corresponding entry (refer to the IP address and hostname), then delete the corresponding line.
For example, in the known_hosts
file, find an entry like this:
1 | github.com,192.30.255.112 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hX ... |
Delete that line.
2. Confirm GitHub’s SSH host fingerprint:
Confirm the current valid Transport Layer Security (TLS) public key fingerprint and SSH public key fingerprint from the GitHub official documentation.
3. Reconnect via SSH and add the new host key:
In the terminal or command prompt, execute 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.”, then SSH authentication has succeeded, and the issue is resolved.
You should now be able to commit code to GitHub without the warning message appearing.
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 for the current repository
If you just 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 following command:
1 | $ git log -1 --pretty=oneline |
The -1
parameter lists only the latest commit.
If you add --decorate
, you will see branch information (also depending on the git version, newer versions have branch information by default):
1 | $ git log -1 --pretty=oneline --decorate |
SSH key login failure
If the SSH connection prompts the following error:
1 | lipengzha@192.168.31.55: Permission denied (publickey,password,keyboard-interactive). |
You can modify the SSH configuration on the server (/etc/ssh/sshd_config
):
1 | RSAAuthentication yes |
Then restart the SSHD service.
View the last modifier of a specific line of code
To view the last committer of the n~m
lines 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 history records
To clear all history records in a Git repository while keeping only the latest commit, you can follow these steps:
Create a new branch:
First, ensure you are in a clean working directory (with no uncommitted changes), then create a new branch.1
git checkout --orphan latest_branch
This command creates a new branch without a 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 main:
Rename the new branch to main.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 records in the remote repository.1
git push -f origin main
This will clear all history records, keeping only the latest commit. This operation is irreversible, and all history records will be permanently deleted, so please ensure you really do not need these history records before performing this operation.
Differences Between Windows and Linux Line Endings
Windows uses carriage return line feed (CRLF, \r\n
), while Linux uses line feed (LF, \n
). If you create or modify a file on Windows and then view it on Linux, Git may think the file has changed due to the difference in 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 a
.gitattributes
file to specify line ending handling for specific files, for example:
1 | * text=auto |
Note: After setting this up, I’ve found that changed files do not display in the working directory in real-time. You can delete this configuration in
C:\User\[USER_NAME]\.gitconfig
.
UPDATE
CheatSheet
Reference Articles
Detailed explanation of Git remote operations
Creating and merging branches
Undoing branch deletion in Git
Reverting changes with git checkout command
Diffing two branches with git diff