SVN to GIT Cheatsheet

From Lazarus wiki
Jump to navigationJump to search

ABOUT

This page contains an introduction to GIT, for users with prior SVN experience.

Alternative to this Page

There is a prior page with similar intent: FPC_git

This alternative page differs in some main points:

  • The alternative is based on the older/established git commands "checkout" and "reset". This page uses "switch" and "restore"
Those older commands are harder to learn/understand. They do not have clean distinction between different tasks.
The makers of GIT introduced the new commands as a replacement. But the new commands are still marked as "experimental". They are stable and safe to use. But they may still have changes in future git versions.
In my opinion the fact that they are easier to learn, compensates well for the off chance that one may later have to learn about potential changes to them.
(Also, even old/established commands can get changed. But less likely)
  • The alternative page teaches "using the index" as the way to go.
This is only one option to use git. As it requires additional steps in several commands, it is less preferable to users coming from svn.
The GIT documentation contains all the steps, how to use GIT without "index". So this is as good a way to use git as any other.
(One still needs to be aware, in case one accidentally access the index.)

Useful to know background

There a few concepts about git, that everyone should at least be aware of.

Please read the page FPC_git_concepts

Setup

Clone, to create a repository

If you go to the gitlab home page of the project https://gitlab.com/freepascal.org/lazarus_testconversion there is a "clone" button, which offers you to put the clone-url into your clipboard.

   git clone https://gitlab.com/freepascal.org/lazarus_testconversion.git  ./lazarus_git
  • The folder lazarus_git will be created for you.
Set up your user details
   cd ./lazarus_git
   git config user.name  martin
   git config user.email martin@email-domain.host
  • Those details will appear on every commit you make
  • If you clone to an other device, you have to apply the settings again.
  • On the same PC, you can make the settings global "git config --global user.email foo@bar.com"
  • Those are not your login details

To view current settings

   git config -l


Adding your credentials (required to push commits)

In order for your credentials to be remembered run (you may check "git config -l" first, to see if already set)

  • on Linux / Mac)
   git config --global credential.helper store
  • On windows credentials should be stored in the Windows Credential storage.
(Unable to test currently / should be either manager-core or wincred)
   git config --global credential.helper wincred
   

Then run

   git push

This will add you for your credentials.

  • Enter the username you use to access the gitlab webpage. You can also use the primary email with which you registered.
  • Enter your password for the gitlab webpage
  • If you do not want to use your main password it is advised to use an "access token" as password (the username remains as is).
Log into the gitlab webpage and in your user settings section find the "access token" page. Scope should be read/write repository.

Recommended

Some advised defaults.

For svn users, it is often desirable to keep the commit history "flat". That means within a single branch, you want to avoid having 2 strains of commits (diverge) and then merge together again.
By default git may do exactly that, when you have local commits, and get new commits from the server.

To tell git you prefer a single chain of commits (in each branch) run

   git config pull.ff only

Depending on preference you can alternatively run (see details on fast-forward, rebase, and merge)

   git config pull.rebase true


Having added this config allows you to drop the matching argument from "git pull" in the command map below. You can then simply run "git pull" (depending on which config you made, and which option would be advised)

Command MAP

svn checkout

   git clone <url> <directory>

The amount of history can be limited with (for the master branch, the given branch, all branches)

   git clone --depth <number> <url> <directory>
   git clone --depth <number> --branch <somebranch> <url> <directory>

svn update

See the notes on fast-forward, rebase and merging. To keep a flat commit line use --ff-only or –rebase. To update the current branch:

   git pull --ff-only

If you have unsaved (i.e. uncommitted) changes in files that need to be updated:

   git stash save
   git pull --ff-only
   git stash pop

You can always try to "pull" without "stash". If it is not possible, git will tell you. You can then use the command sequence with "stash" and "pop".

"git stash pop" may give a "conflict" error, if the changes can not be merged. You then need to resolve this yourself (same in svn)

Light bulb  Note: "svn checkout" into a checkout with local changes will force the checkout, and introduce any conflicts immediately.
"git pull --ff-only" will not do that. It will tell you that there would be a conflict. You can then decide to deal with it now or later.

You can also use

   git pull --rebase

instead of “--ff-only” if you have local commits. This will move your local commits, behind any new commits pulled from the server.


To update the all remote branch(es), without touching the local branches.

   git fetch --all

After this, you can use "git log origin/branch" to see what changed remotely.

  • You may also find examples with: "git remote update"

svn update -r <rev>

To go to an earlier revision, in git you create a branch at this earlier commit.

   git switch -c <local-branch-name> <commit-hash>

Or to go (let’s say) 3 commits back

   git switch -c <local-branch-name> HEAD~3

svn switch

Light bulb  Note: "svn switch" has to retrieve the other branch from the server, and may include an update to its latest version.
In GIT all the branches are already downloaded. So switching only needs to update the files in your working directory.
If you wish to get the latest version from the server you can run either:

  • git fetch --all ## before you switch
  • git pull --ff-only ## after the switch
  git switch <local-branch-name>

will either:

  • switch to an existing local branch
  • create a new local branch, from a remote branch of the same name

To force create a new local branch (this will give an error, if the local branch already exists):

   git switch -c <local-branch-name> <remote-branch-name>
Warning-icon.png

Warning: With -C (uppercase) you can force the creation, abandoning the old local branch. This may loose the data on any commits existing on the old local branch. (This can be used, if the commits in question are hold by yet another local branch)

Light bulb  Note: Some pages on the internet will use “git checkout”. Do not use this.

svn commit

In git your commit goes to your local branch. To do the full svn commit, you need to commit and push in git.

   git commit -m ‘message’ file1 file2 file2 …
   git push

You can collect as many commits as you want before you push them.

New files need to be “add”ed first.

Light bulb  Note: Using git commit, without specifying files will commit the change you “index”ed. Read the section on the index. Indexed files may have different content, than the file on your hard-drive. Using commit with a list of files will always commit the listed files only and with the content you last saved to your hard-drive.

To commit all modified files (as on your hard-drive), including any file you deleted (new files still need to be added first):

   git commit -m ‘my message’ -a

svn add

  git add -N <file>
Light bulb  Note: If you forget the “-N” the notes on the “index” apply. In that case, if you make further changes to the file, then you must specify the file on the “git commit” command line again.

svn revert

   git restore <file>
   git restore <folder>
   git restore **/<file>

<folder> includes all subfolders. So does folder/* because * matches the names of subfolders.

"**" means any path of zero, one or more levels. "anywhere within"

Revert to the content of the previous commit:

   git restore --source HEAD~1 <file>
Light bulb  Note: Some pages on the internet will use “git checkout”. Do not use this.

svn rename

   git mv <source> <dest>

svn delete

   git rm <file>
  • Files that are modified can only be deleted by force -f
  • For recursive removal use -r

svn log

   git log -n <number_of_entries>
   git log --oneline –graph -n <number_of_entries>

Instead of a number of commits, you can give a range of revisions (by hash, branchname, …). This also allows you do get a log of a different branch.

   git log HEAD~10..HEAD
   git log branch~10..branch

svn ls

for local branches

   git branch

to include remote branches

   git branch -r

to be verbose

   git branch -v

To see tags

   git tag

svn diff

Light bulb  Note: the first of the below commands can be affected by the “index”, if you use the index.
  git diff 
  git diff –no-index
 

To compare with other commits

  git diff <hash>
  git diff HEAD~10

svn resolved

Template:Todo

   git add <file>
   git commit <file>

Conflicted files can be seen using git status.

Conflicted lines in the code are marked similar to svn, using <<<, >>>>, ==== sections. You can edit the file, to merge the changes by hand. Or you can use either of the following to only keep your changes, or the changes of the other:

   git checkout --ours file
   git checkout --theirs file

You can restore the conflict info in the file with

   git checkout --merge file

You can switch between the above states as often as you like. But be aware that any changes you edited in the file will be reset with the above checkouts. Once you have the files in an acceptable state, you can mark them as resolved resolved with either:

   git add file
   git reset file

The difference between add and reset is that add puts the file on the index(staged), and reset makes it an unstaged file. In both cases the file on the disk is left with the <<< >>> === until you edit it. If the conflict was between deleting or keeping a file you can use git add or git rm to keep or remove the file.

Conflicts during update

If the conflict occurred during a “git pull”, then you still have to execute the “git stash pop” (You should have seen a message that it failed) If the conflict occurred after a “git stash pop”, then the stash is also still existent, despite being applied too. To verify view the stash “git stash list”, and to remove the last of the list “git stash drop” (optional specify the hash from the list).

Other git commands

git status

   git status

Will list files grouped in the following sections. Files can be listed as modified, deleted, new file, renamed.

  • Changes to be committed
This is the section explained below as “index”. This are files that you specified to git add, git rm, git mv.
Note that files can be listed again in other sections. Indicating that they had further changes after they where “added” (or rm/mv).
  • Unmerged Path
Files that are in conflicted state
  • Changes not staged for committed
Modified files (before add,rm,mv)
  • Untracked files
Anything in your working tree that is not known to git (yet)

If you have plenty of "untracked files" you can omit them by using

   git status -uno

Fast-Forward, Rebase and Merge

"Tracking Branches" - Connect your local branch and its remote counter-part (aka "Upstream")

A.k.a
Upstream branch
Please read
https://wiki.lazarus.freepascal.org/FPC_git_concepts#Local_and_Remote_Branches

What is tracking

Say you clone a remote git repository. This repository has the following branches

  • master
  • fixes-2

Those branches are reflected in your local git as ("origin" is the default name for your remote server / this assumes you use this default)

  • remote/origin/master
  • remote/origin/fixes-2

You can checkout to the commits in those branches. But you can not commit to those branches. As they represent the last known state on the server, the only way to update them, is by updating the server. Either you pushing to the server, or you pulling someone else's commits from the server.


Tracking branch

In order to commit, you need (a) local branch(es).

   git switch fixes-2
  • If you did not yet have this branch, this will create a local fixes-2 (no prefix) branch for you. If newly created the branch will be at the same commit as origin/fixes-2 (short form).
  • If the branch already existed, you may have to fast-forward or rebase it first (see the section on ff/rebase).

Because the local name matched the remote name, and because you used "git switch", the local branch will have been set to track the remote.


Now you have may have commits like this

   A => B => C (fixes-2, origin/fixes-2)

Edit a file in the worktree and commit it, you get

   A => B => C (origin/fixes-2) => D (fixes-2)

This is described as "Your local branch is one commit ahead".

You can now push your local branch to the remote server

   git push

This will add your commit "D" to the server, and update the remote branch

   A => B => C => D (origin/fixes-2, fixes-2)


Non tracking branch

You can create a new branch, at the current checked out commit.

   git switch -c new-branch-foo
   A => B => C => D (origin/fixes-2, fixes-2, new-branch-foo)

This branch does not know about origin/fixes-2. It is not tracking.

Now again edit a file and commit. You get

   A => B => C => D (origin/fixes-2, fixes-2) => E (new-branch-foo)

If you now try to do

   git push

you will get an error. GIT does not know to which branch on the server it should send your commit.

For a non tracking branch you need to do:

   git push origin fixes-2

If instead of updating the existing remote/fixes-2 branch, you want to create a new branch on the server the you can do:

   git push origin new-branch-foo

Here "new-branch-foo" is not the local name, but the name on the server, that will then be reflected an remote/origin/new-branch-foo.

So you could also do

   git push origin b-foo

And your local branch new-branch-foo would create remote/origin/b-foo

Setting / Un-Setting tracking

In the above example the local branch of the same name as the remote was tracking. And ideally that is how you want it to be. But there is no enforcement. While "git switch" may do this for you when creating a new branch, it is possible to have a local "foo" branch that does not track "origin/foo"

In order to make a local branch tracking you can give an argument to push.

   git push  --set-upstream  origin  remote-branch
   git push  -u              origin  remote-branch

When you create a new local branch you can do any of those

   git switch --no-track  -c  local-name  origin/remote-name
   git switch --track  -c  local-name  origin/remote-name
   git switch -t       -c  local-name  origin/remote-name

Nice to know / for later

git worktree / having more than one checkout

Just to mention here, if you want different branches/commits checked out in different directory, you do not need a 2nd clone. You can have several working dirs from one clone. See https://git-scm.com/docs/git-worktree