-
1. Ăvod
-
2. Zåklady pråce se systémem Git
-
3. VÄtve v systĂ©mu Git
- 3.1 VÄtve v kostce
- 3.2 ZĂĄklady vÄtvenĂ a sluÄovĂĄnĂ
- 3.3 SprĂĄva vÄtvĂ
- 3.4 Postupy pĆi prĂĄci s vÄtvemi
- 3.5 VzdĂĄlenĂ© vÄtve
- 3.6 PĆesklĂĄdĂĄnĂ
- 3.7 ShrnutĂ
-
4. Git na serveru
- 4.1 Protokoly
- 4.2 ZprovoznÄnĂ Gitu na serveru
- 4.3 GenerovĂĄnĂ veĆejnĂ©ho klĂÄe SSH
- 4.4 NastavenĂ serveru
- 4.5 DĂ©mon Git
- 4.6 ChytrĂœ HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 MoĆŸnosti hostovĂĄnĂ u tĆetĂ strany
- 4.10 ShrnutĂ
-
5. DistribuovanĂœ Git
-
6. GitHub
-
7. Git Tools
- 7.1 Revision Selection
- 7.2 Interactive Staging
- 7.3 Stashing and Cleaning
- 7.4 Signing Your Work
- 7.5 Searching
- 7.6 Rewriting History
- 7.7 Reset Demystified
- 7.8 Advanced Merging
- 7.9 Rerere
- 7.10 LadÄnĂ v systĂ©mu Git
- 7.11 Submodules
- 7.12 Bundling
- 7.13 Replace
- 7.14 Credential Storage
- 7.15 ShrnutĂ
-
8. Customizing Git
- 8.1 Git Configuration
- 8.2 Atributy Git
- 8.3 Git Hooks
- 8.4 An Example Git-Enforced Policy
- 8.5 ShrnutĂ
-
9. Git a ostatnà systémy
- 9.1 Git as a Client
- 9.2 Migrating to Git
- 9.3 ShrnutĂ
-
10. Git Internals
- 10.1 Plumbing and Porcelain
- 10.2 Git Objects
- 10.3 Git References
- 10.4 BalĂÄkovĂ© soubory
- 10.5 The Refspec
- 10.6 PĆenosovĂ© protokoly
- 10.7 SprĂĄva a obnova dat
- 10.8 Environment Variables
- 10.9 ShrnutĂ
-
A1. Appendix A: Git in Other Environments
- A1.1 Graphical Interfaces
- A1.2 Git in Visual Studio
- A1.3 Git in Eclipse
- A1.4 Git in Bash
- A1.5 Git in Zsh
- A1.6 Git in Powershell
- A1.7 ShrnutĂ
-
A2. Appendix B: Embedding Git in your Applications
- A2.1 Command-line Git
- A2.2 Libgit2
- A2.3 JGit
-
A3. Appendix C: Git Commands
- A3.1 Setup and Config
- A3.2 Getting and Creating Projects
- A3.3 Basic Snapshotting
- A3.4 Branching and Merging
- A3.5 Sharing and Updating Projects
- A3.6 Inspection and Comparison
- A3.7 Debugging
- A3.8 Patching
- A3.9 Email
- A3.10 External Systems
- A3.11 Administration
- A3.12 Plumbing Commands
7.11 Git Tools - Submodules
Submodules
Äasto se stĂĄvĂĄ, ĆŸe pracujete na jednom projektu, ale na chvĂli si potĆebujete odskoÄit do jinĂ©ho. JednĂĄ se tĆeba o knihovnu, kterou vyvinula tĆetĂ strana, nebo kterou vyvĂjĂte oddÄlenÄ a pouĆŸĂvĂĄte ji v nÄkolika nadĆazenĂœch projektech. V obou pĆĂpadech se budete potĂœkat se stejnĂœm problĂ©mem: oba projekty chcete zachovat samostatnĂ©, a pĆesto potĆebujete pouĆŸĂvat jeden v rĂĄmci druhĂ©ho.
UveÄme malĂœ pĆĂklad. Programujete webovĂ© strĂĄnky a vytvĂĄĆĂte kanĂĄly Atom. MĂsto abyste psali vlastnĂ zdrojovĂœ kĂłd ke kanĂĄlĆŻm Atom, rozhodnete se pouĆŸĂt knihovnu. PravdÄpodobnÄ budete muset pouĆŸĂt tento kĂłd ze sdĂlenĂ© knihovny, jako CPAN install nebo Ruby gem, nebo zkopĂrovat zdrojovĂœ kĂłd do vlastnĂho stromu projektu. ProblĂ©m s pouĆŸitĂm knihovny je ten, ĆŸe je obtĂĆŸnĂ© knihovnu jakĂœmkoli zpĆŻsobem upravit a Äasto jeĆĄtÄ tÄĆŸĆĄĂ ji nasadit, protoĆŸe se musĂte ujistit, ĆŸe ji mĂĄ k dispozici kaĆŸdĂœ klient. ProblĂ©mem s pĆevzetĂm zdrojovĂ©ho kĂłdu do vlastnĂho projektu bĂœvĂĄ, ĆŸe jakĂ©koli uĆŸivatelskĂ© zmÄny, kterĂ© provedete, se obtĂĆŸnÄ zaÄleĆujĂ, pokud se objevĂ novÄjĆĄĂ zmÄny.
Git nabĂzĂ jako ĆeĆĄenĂ tohoto problĂ©mu nĂĄstroj submodulĆŻ. Submoduly umoĆŸĆujĂ uchovĂĄvat repozitĂĄĆ Git jako podadresĂĄĆ jinĂ©ho repozitĂĄĆe Git. Do svĂ©ho projektu tak mĆŻĆŸete naklonovat jinĂœ repozitĂĄĆ a uchovĂĄvat revize oddÄlenĂ©.
Starting with Submodules
Weâll walk through developing a simple project that has been split up into a main project and a few sub-projects.
Letâs start by adding an existing Git repository as a submodule of the repository that weâre working on.
To add a new submodule you use the git submodule add
command with the absolute or relative URL of the project you would like to start tracking.
In this example, weâll add a library called âDbConnectorâ.
$ git submodule add https://github.com/chaconinc/DbConnector
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
By default, submodules will add the subproject into a directory named the same as the repository, in this case âDbConnectorâ. You can add a different path at the end of the command if you want it to go elsewhere.
If you run git status
at this point, youâll notice a few things.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: DbConnector
First you should notice the new .gitmodules
file.
JednĂĄ se o konfiguraÄnĂ soubor, v nÄmĆŸ je uloĆŸeno mapovĂĄnĂ mezi adresou URL projektu a lokĂĄlnĂm podadresĂĄĆem, do nÄjĆŸ jste stĂĄhli repozitĂĄĆ.
[submodule "DbConnector"]
path = DbConnector
url = https://github.com/chaconinc/DbConnector
MĂĄte-li submodulĆŻ vĂce, bude v tomto souboru nÄkolik zĂĄznamĆŻ.
Za zmĂnku stojĂ, ĆŸe je tento soubor verzovĂĄn spolu s ostatnĂmi soubory, podobnÄ jako tĆeba soubor .gitignore
.
Soubor se odesĂlĂĄ a stahuje se zbytkem projektu.
OstatnĂ uĆŸivatelĂ©, kteĆĂ budou tento projekt klonovat, dĂky tomu zjistĂ, kde najdou projekty submodulĆŻ.
Note
|
Since the URL in the .gitmodules file is what other people will first try to clone/fetch from, make sure to use a URL that they can access if possible.
For example, if you use a different URL to push to than others would to pull from, use the one that others have access to.
You can overwrite this value locally with |
The other listing in the git status
output is the project folder entry.
Pokud na ni pouĆŸijete pĆĂkaz git diff
, uvidĂte zajĂmavou vÄc:
$ git diff --cached DbConnector
diff --git a/DbConnector b/DbConnector
new file mode 160000
index 0000000..c3f01dc
--- /dev/null
+++ b/DbConnector
@@ -0,0 +1 @@
+Subproject commit c3f01dc8862123d317dd46284b05b6892c7b29bc
Although DbConnector
is a subdirectory in your working directory, Git sees it as a submodule and doesnât track its contents when youâre not in that directory.
Instead, Git sees it as a particular commit from that repository.
If you want a little nicer diff output, you can pass the --submodule
option to git diff
.
$ git diff --cached --submodule
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..71fc376
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "DbConnector"]
+ path = DbConnector
+ url = https://github.com/chaconinc/DbConnector
Submodule DbConnector 0000000...c3f01dc (new submodule)
JestliĆŸe zapĂĆĄete revizi, zobrazĂ se pĆibliĆŸnÄ toto:
$ git commit -am 'added DbConnector module'
[master fb9093c] added DbConnector module
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 DbConnector
Notice the 160000
mode for the DbConnector
entry.
JednĂĄ se o speciĂĄlnĂ reĆŸim systĂ©mu Git, kterĂœ udĂĄvĂĄ, ĆŸe revizi zaznamenĂĄvĂĄte jako adresĂĄĆ, ne jako podadresĂĄĆ nebo soubor.
Cloning a Project with Submodules
Here weâll clone a project with a submodule in it. When you clone such a project, by default you get the directories that contain submodules, but none of the files within them yet:
$ git clone https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
$ cd MainProject
$ ls -la
total 16
drwxr-xr-x 9 schacon staff 306 Sep 17 15:21 .
drwxr-xr-x 7 schacon staff 238 Sep 17 15:21 ..
drwxr-xr-x 13 schacon staff 442 Sep 17 15:21 .git
-rw-r--r-- 1 schacon staff 92 Sep 17 15:21 .gitmodules
drwxr-xr-x 2 schacon staff 68 Sep 17 15:21 DbConnector
-rw-r--r-- 1 schacon staff 756 Sep 17 15:21 Makefile
drwxr-xr-x 3 schacon staff 102 Sep 17 15:21 includes
drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 scripts
drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 src
$ cd DbConnector/
$ ls
$
The DbConnector
directory is there, but empty.
Budete muset pouĆŸĂt dva pĆĂkazy: git submodule init
k inicializaci lokĂĄlnĂho konfiguraÄnĂho souboru a git submodule update
k vyzvednutĂ vĆĄech dat z tohoto projektu a checkoutu pĆĂsluĆĄnĂ© revize uvedenĂ© ve vaĆĄem superprojektu:
$ git submodule init
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
$ git submodule update
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'
Now your DbConnector
subdirectory is at the exact state it was in when you committed earlier.
There is another way to do this which is a little simpler, however.
If you pass --recursive
to the git clone
command, it will automatically initialize and update each submodule in the repository.
$ git clone --recursive https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'
Working on a Project with Submodules
Now we have a copy of a project with submodules in it and will collaborate with our teammates on both the main project and the submodule project.
Pulling in Upstream Changes
The simplest model of using submodules in a project would be if you were simply consuming a subproject and wanted to get updates from it from time to time but were not actually modifying anything in your checkout. Letâs walk through a simple example there.
If you want to check for new work in a submodule, you can go into the directory and run git fetch
and git merge
the upstream branch to update the local code.
$ git fetch
From https://github.com/chaconinc/DbConnector
c3f01dc..d0354fc master -> origin/master
$ git merge origin/master
Updating c3f01dc..d0354fc
Fast-forward
scripts/connect.sh | 1 +
src/db.c | 1 +
2 files changed, 2 insertions(+)
Now if you go back into the main project and run git diff --submodule
you can see that the submodule was updated and get a list of commits that were added to it.
If you donât want to type --submodule
every time you run git diff
, you can set it as the default format by setting the diff.submodule
config value to âlogâ.
$ git config --global diff.submodule log
$ git diff
Submodule DbConnector c3f01dc..d0354fc:
> more efficient db routine
> better connection routine
If you commit at this point then you will lock the submodule into having the new code when other people update.
There is an easier way to do this as well, if you prefer to not manually fetch and merge in the subdirectory.
If you run git submodule update --remote
, Git will go into your submodules and fetch and update for you.
$ git submodule update --remote DbConnector
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
3f19983..d0354fc master -> origin/master
Submodule path 'DbConnector': checked out 'd0354fc054692d3906c85c3af05ddce39a1c0644'
This command will by default assume that you want to update the checkout to the master
branch of the submodule repository.
You can, however, set this to something different if you want.
For example, if you want to have the DbConnector submodule track that repositoryâs âstableâ branch, you can set it in either your .gitmodules
file (so everyone else also tracks it), or just in your local .git/config
file.
Letâs set it in the .gitmodules
file:
$ git config -f .gitmodules submodule.DbConnector.branch stable
$ git submodule update --remote
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
27cf5d3..c87d55d stable -> origin/stable
Submodule path 'DbConnector': checked out 'c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687'
If you leave off the -f .gitmodules
it will only make the change for you, but it probably makes more sense to track that information with the repository so everyone else does as well.
When we run git status
at this point, Git will show us that we have ânew commitsâ on the submodule.
$ 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: .gitmodules
modified: DbConnector (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
If you set the configuration setting status.submodulesummary
, Git will also show you a short summary of changes to your submodules:
$ git config status.submodulesummary 1
$ 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: .gitmodules
modified: DbConnector (new commits)
Submodules changed but not updated:
* DbConnector c3f01dc...c87d55d (4):
> catch non-null terminated lines
At this point if you run git diff
we can see both that we have modified our .gitmodules
file and also that there are a number of commits that weâve pulled down and are ready to commit to our submodule project.
$ git diff
diff --git a/.gitmodules b/.gitmodules
index 6fc0b3d..fd1cc29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submodule "DbConnector"]
path = DbConnector
url = https://github.com/chaconinc/DbConnector
+ branch = stable
Submodule DbConnector c3f01dc..c87d55d:
> catch non-null terminated lines
> more robust error handling
> more efficient db routine
> better connection routine
This is pretty cool as we can actually see the log of commits that weâre about to commit to in our submodule.
Once committed, you can see this information after the fact as well when you run git log -p
.
$ git log -p --submodule
commit 0a24cfc121a8a3c118e0105ae4ae4c00281cf7ae
Author: Scott Chacon <schacon@gmail.com>
Date: Wed Sep 17 16:37:02 2014 +0200
updating DbConnector for bug fixes
diff --git a/.gitmodules b/.gitmodules
index 6fc0b3d..fd1cc29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submodule "DbConnector"]
path = DbConnector
url = https://github.com/chaconinc/DbConnector
+ branch = stable
Submodule DbConnector c3f01dc..c87d55d:
> catch non-null terminated lines
> more robust error handling
> more efficient db routine
> better connection routine
Git will by default try to update all of your submodules when you run git submodule update --remote
so if you have a lot of them, you may want to pass the name of just the submodule you want to try to update.
Working on a Submodule
Itâs quite likely that if youâre using submodules, youâre doing so because you really want to work on the code in the submodule at the same time as youâre working on the code in the main project (or across several submodules). Otherwise you would probably instead be using a simpler dependency management system (such as Maven or Rubygems).
So now letâs go through an example of making changes to the submodule at the same time as the main project and committing and publishing those changes at the same time.
So far, 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 do two things.
You need to go into each submodule and check out a branch to work on.
Then you need to tell Git what to do if you have made changes and then git submodule update --remote
pulls in new work from upstream.
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.
First of all, letâs go into our submodule directory and check out a branch.
$ git checkout stable
Switched to branch 'stable'
Letâs try it with the âmergeâ option.
To specify it manually, we can just add the --merge
option to our update
call.
Here weâll see that there was a change on the server for this submodule and it gets merged in.
$ git submodule update --remote --merge
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
c87d55d..92c7337 stable -> origin/stable
Updating c87d55d..92c7337
Fast-forward
src/main.c | 1 +
1 file changed, 1 insertion(+)
Submodule path 'DbConnector': merged in '92c7337b30ef9e0893e758dac2459d07362ab5ea'
If we go into the DbConnector directory, we have the new changes already merged into our local stable
branch.
Now letâs see what happens when we make our own local change to the library and someone else pushes another change upstream at the same time.
$ cd DbConnector/
$ vim src/db.c
$ git commit -am 'unicode support'
[stable f906e16] unicode support
1 file changed, 1 insertion(+)
Now if we update our submodule we can see what happens when we have made a local change and upstream also has a change we need to incorporate.
$ git submodule update --remote --rebase
First, rewinding head to replay your work on top of it...
Applying: unicode support
Submodule path 'DbConnector': rebased into '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'
If you forget the --rebase
or --merge
, Git will just update the submodule to whatever is on the server and reset your project to a detached HEAD state.
$ git submodule update --remote
Submodule path 'DbConnector': checked out '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'
If this happens, donât worry, you can simply go back into the directory and check out your branch again (which will still contain your work) and merge or rebase origin/stable
(or whatever remote branch you want) manually.
If you havenât committed your changes in your submodule and you run a submodule update that would cause issues, Git will fetch the changes but not overwrite unsaved work in your submodule directory.
$ git submodule update --remote
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 4 (delta 0)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
5d60ef9..c75e92a stable -> origin/stable
error: Your local changes to the following files would be overwritten by checkout:
scripts/setup.sh
Please, commit your changes or stash them before you can switch branches.
Aborting
Unable to checkout 'c75e92a2b3855c9e5b66f915308390d9db204aca' in submodule path 'DbConnector'
If you made changes that conflict with something changed upstream, Git will let you know when you run the update.
$ git submodule update --remote --merge
Auto-merging scripts/setup.sh
CONFLICT (content): Merge conflict in scripts/setup.sh
Recorded preimage for 'scripts/setup.sh'
Automatic merge failed; fix conflicts and then commit the result.
Unable to merge 'c75e92a2b3855c9e5b66f915308390d9db204aca' in submodule path 'DbConnector'
You can go into the submodule directory and fix the conflict just as you normally would.
Publishing Submodule Changes
Now we have some changes in our submodule directory. Some of these were brought in from upstream by our updates and others were made locally and arenât available to anyone else yet as we havenât pushed them yet.
$ git diff
Submodule DbConnector c87d55d..82d2ad3:
> Merge from origin/stable
> updated setup script
> unicode support
> remove unnecessary method
> add new option for conn pooling
If we commit in the main project and push it up without pushing the submodule changes up as well, other people who try to check out our changes are going to be in trouble since they will have no way to get the submodule changes that are depended on. Those changes will only exist on our local copy.
In order to make sure this doesnât happen, you can ask Git to check that all your submodules have been pushed properly before pushing the main project.
The git push
command takes the --recurse-submodules
argument which can be set to either âcheckâ or âon-demandâ.
The âcheckâ option will make push
simply fail if any of the committed submodule changes havenât been pushed.
$ git push --recurse-submodules=check
The following submodule paths contain changes that can
not be found on any remote:
DbConnector
Please try
git push --recurse-submodules=on-demand
or cd to the path and use
git push
to push them to a remote.
As you can see, it also gives us some helpful advice on what we might want to do next. The simple option is to go into each submodule and manually push to the remotes to make sure theyâre externally available and then try this push again.
The other option is to use the âon-demandâ value, which will try to do this for you.
$ git push --recurse-submodules=on-demand
Pushing submodule 'DbConnector'
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done.
Total 9 (delta 3), reused 0 (delta 0)
To https://github.com/chaconinc/DbConnector
c75e92a..82d2ad3 stable -> stable
Counting objects: 2, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 266 bytes | 0 bytes/s, done.
Total 2 (delta 1), reused 0 (delta 0)
To https://github.com/chaconinc/MainProject
3d6d338..9a377d1 master -> master
As you can see there, Git went into the DbConnector module and pushed it before pushing the main project. If that submodule push fails for some reason, the main project push will also fail.
Merging Submodule Changes
If you change a submodule reference at the same time as someone else, you may run into some problems. That is, if the submodule histories have diverged and are committed to diverging branches in a superproject, it may take a bit of work for you to fix.
If one of the commits is a direct ancestor of the other (a fast-forward merge), then Git will simply choose the latter for the merge, so that works fine.
Git will not attempt even a trivial merge for you, however. If the submodule commits diverge and need to be merged, you will get something that looks like this:
$ git pull
remote: Counting objects: 2, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 2 (delta 1), reused 2 (delta 1)
Unpacking objects: 100% (2/2), done.
From https://github.com/chaconinc/MainProject
9a377d1..eb974f8 master -> origin/master
Fetching submodule DbConnector
warning: Failed to merge submodule DbConnector (merge following commits not found)
Auto-merging DbConnector
CONFLICT (submodule): Merge conflict in DbConnector
Automatic merge failed; fix conflicts and then commit the result.
So basically what has happened here is that Git has figured out that the two branches record points in the submoduleâs history that are divergent and need to be merged. It explains it as âmerge following commits not foundâ, which is confusing but weâll explain why that is in a bit.
To solve the problem, you need to figure out what state the submodule should be in.
Strangely, Git doesnât really give you much information to help out here, not even the SHA-1s of the commits of both sides of the history.
Fortunately, itâs simple to figure out.
If you run git diff
you can get the SHA-1s of the commits recorded in both branches you were trying to merge.
$ git diff
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
So, in this case, eb41d76
is the commit in our submodule that we had and c771610
is the commit that upstream had.
If we go into our submodule directory, it should already be on eb41d76
as the merge would not have touched it.
If for whatever reason itâs not, you can simply create and checkout a branch pointing to it.
What is important is the SHA-1 of the commit from the other side. This is what youâll have to merge in and resolve. You can either just try the merge with the SHA-1 directly, or you can create a branch for it and then try to merge that in. We would suggest the latter, even if only to make a nicer merge commit message.
So, we will go into our submodule directory, create a branch based on that second SHA-1 from git diff
and manually merge.
$ cd DbConnector
$ git rev-parse HEAD
eb41d764bccf88be77aced643c13a7fa86714135
$ git branch try-merge c771610
(DbConnector) $ git merge try-merge
Auto-merging src/main.c
CONFLICT (content): Merge conflict in src/main.c
Recorded preimage for 'src/main.c'
Automatic merge failed; fix conflicts and then commit the result.
We got an actual merge conflict here, so if we resolve that and commit it, then we can simply update the main project with the result.
$ vim src/main.c (1)
$ git add src/main.c
$ git commit -am 'merged our changes'
Recorded resolution for 'src/main.c'.
[master 9fd905e] merged our changes
$ cd .. (2)
$ git diff (3)
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
@@@ -1,1 -1,1 +1,1 @@@
- Subproject commit eb41d764bccf88be77aced643c13a7fa86714135
-Subproject commit c77161012afbbe1f58b5053316ead08f4b7e6d1d
++Subproject commit 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a
$ git add DbConnector (4)
$ git commit -m "Merge Tom's Changes" (5)
[master 10d2c60] Merge Tom's Changes
-
First we resolve the conflict
-
Then we go back to the main project directory
-
We can check the SHA-1s again
-
Resolve the conflicted submodule entry
-
Commit our merge
It can be a bit confusing, but itâs really not very hard.
Interestingly, there is another case that Git handles. If a merge commit exists in the submodule directory that contains both commits in its history, Git will suggest it to you as a possible solution. It sees that at some point in the submodule project, someone merged branches containing these two commits, so maybe youâll want that one.
This is why the error message from before was âmerge following commits not foundâ, because it could not do this. Itâs confusing because who would expect it to try to do this?
If it does find a single acceptable merge commit, youâll see something like this:
$ git merge origin/master
warning: Failed to merge submodule DbConnector (not fast-forward)
Found a possible merge resolution for the submodule:
9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a: > merged our changes
If this is correct simply add it to the index for example
by using:
git update-index --cacheinfo 160000 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a "DbConnector"
which will accept this suggestion.
Auto-merging DbConnector
CONFLICT (submodule): Merge conflict in DbConnector
Automatic merge failed; fix conflicts and then commit the result.
What itâs suggesting that you do is to update the index like you had run git add
, which clears the conflict, then commit. You probably shouldnât do this though. You can just as easily go into the submodule directory, see what the difference is, fast-forward to this commit, test it properly, and then commit it.
$ cd DbConnector/
$ git merge 9fd905e
Updating eb41d76..9fd905e
Fast-forward
$ cd ..
$ git add DbConnector
$ git commit -am 'Fast forwarded to a common submodule child'
This accomplishes the same thing, but at least this way you can verify that it works and you have the code in your submodule directory when youâre done.
Submodule Tips
There are a few things you can do to make working with submodules a little easier.
Submodule Foreach
There is a foreach
submodule command to run some arbitrary command in each submodule.
This can be really helpful if you have a number of submodules in the same project.
For example, letâs say we want to start a new feature or do a bugfix and we have work going on in several submodules. We can easily stash all the work in all our submodules.
$ git submodule foreach 'git stash'
Entering 'CryptoLibrary'
No local changes to save
Entering 'DbConnector'
Saved working directory and index state WIP on stable: 82d2ad3 Merge from origin/stable
HEAD is now at 82d2ad3 Merge from origin/stable
Then we can create a new branch and switch to it in all our submodules.
$ git submodule foreach 'git checkout -b featureA'
Entering 'CryptoLibrary'
Switched to a new branch 'featureA'
Entering 'DbConnector'
Switched to a new branch 'featureA'
You get the idea. One really useful thing you can do is produce a nice unified diff of what is changed in your main project and all your subprojects as well.
$ git diff; git submodule foreach 'git diff'
Submodule DbConnector contains modified content
diff --git a/src/main.c b/src/main.c
index 210f1ae..1f0acdc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -245,6 +245,8 @@ static int handle_alias(int *argcp, const char ***argv)
commit_pager_choice();
+ url = url_decode(url_orig);
+
/* build alias_argv */
alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
alias_argv[0] = alias_string + 1;
Entering 'DbConnector'
diff --git a/src/db.c b/src/db.c
index 1aaefb6..5297645 100644
--- a/src/db.c
+++ b/src/db.c
@@ -93,6 +93,11 @@ char *url_decode_mem(const char *url, int len)
return url_decode_internal(&url, len, NULL, &out, 0);
}
+char *url_decode(const char *url)
+{
+ return url_decode_mem(url, strlen(url));
+}
+
char *url_decode_parameter_name(const char **query)
{
struct strbuf out = STRBUF_INIT;
Here we can see that weâre defining a function in a submodule and calling it in the main project. This is obviously a simplified example, but hopefully it gives you an idea of how this may be useful.
Useful Aliases
You may want to set up some aliases for some of these commands as they can be quite long and you canât set configuration options for most of them to make them defaults. We covered setting up Git aliases in Aliasy v Gitu, but here is an example of what you may want to set up if you plan on working with submodules in Git a lot.
$ git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
$ git config alias.spush 'push --recurse-submodules=on-demand'
$ git config alias.supdate 'submodule update --remote --merge'
This way you can simply run git supdate
when you want to update your submodules, or git spush
to push with submodule dependency checking.
Problémy se submoduly
PouĆŸĂvĂĄnĂ submodulĆŻ se vĆĄak vĆŸdy neobejde bez zĂĄdrhelĆŻ.
For instance switching branches with submodules in them can also be tricky. VytvoĆĂte-li novou vÄtev, pĆidĂĄte do nĂ submodul a potĂ© pĆepnete zpÄt na vÄtev bez tohoto submodulu, nenĂ adresĂĄĆ submodulu stĂĄle jeĆĄtÄ sledovĂĄn:
$ git checkout -b add-crypto
Switched to a new branch 'add-crypto'
$ git submodule add https://github.com/chaconinc/CryptoLibrary
Cloning into 'CryptoLibrary'...
...
$ git commit -am 'adding crypto library'
[add-crypto 4445836] adding crypto library
2 files changed, 4 insertions(+)
create mode 160000 CryptoLibrary
$ git checkout master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
CryptoLibrary/
nothing added to commit but untracked files present (use "git add" to track)
Removing the directory isnât difficult, but it can be a bit confusing to have that in there.
If you do remove it and then switch back to the branch that has that submodule, you will need to run submodule update --init
to repopulate it.
$ git clean -fdx
Removing CryptoLibrary/
$ git checkout add-crypto
Switched to branch 'add-crypto'
$ ls CryptoLibrary/
$ git submodule update --init
Submodule path 'CryptoLibrary': checked out 'b8dda6aa182ea4464f3f3264b11e0268545172af'
$ ls CryptoLibrary/
Makefile includes scripts src
Again, not really very difficult, but it can be a little confusing.
The other main caveat that many people run into involves switching from subdirectories to submodules.
Pokud jste ve svĂ©m projektu sledovali soubory a chcete je pĆesunout do submodulu, musĂte bĂœt velmi opatrnĂ, abyste si Git proti sobÄ nepoĆĄtvali.
Assume that you have files in a subdirectory of your project, and you want to switch it to a submodule.
JestliĆŸe odstranĂte podadresĂĄĆ a spustĂte pĆĂkaz submodule add
, Git vĂĄm vynadĂĄ:
$ rm -Rf CryptoLibrary/
$ git submodule add https://github.com/chaconinc/CryptoLibrary
'CryptoLibrary' already exists in the index
You have to unstage the CryptoLibrary
directory first.
Proto ho musĂte nejprve vrĂĄtit, aĆŸ potom mĆŻĆŸete pĆidat submodul:
$ git rm -r CryptoLibrary
$ git submodule add https://github.com/chaconinc/CryptoLibrary
Cloning into 'CryptoLibrary'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
NynĂ pĆedpoklĂĄdejme, ĆŸe toto vĆĄe se odehrĂĄlo ve vÄtvi. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule â you get this error:
$ git checkout master
error: The following untracked working tree files would be overwritten by checkout:
CryptoLibrary/Makefile
CryptoLibrary/includes/crypto.h
...
Please move or remove them before you can switch branches.
Aborting
You can force it to switch with checkout -f
, but be careful that you donât have unsaved changes in there as they could be overwritten with that command.
$ git checkout -f master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'
Then, when you switch back, you get an empty CryptoLibrary
directory for some reason and git submodule update
may not fix it either.
You may need to go into your submodule directory and run a git checkout .
to get all your files back.
You could run this in a submodule foreach
script to run it for multiple submodules.
Itâs important to note that submodules these days keep all their Git data in the top projectâs .git
directory, so unlike much older versions of Git, destroying a submodule directory wonât lose any commits or branches that you had.
With these tools, submodules can be a fairly simple and effective method for developing on several related but still separate projects simultaneously.