Skip to content

Commit ab17559

Browse files
authored
CONDEC-822: Maintain changed files and their links to Jira issues in the knowledge graph as part of git fetch (#365)
* Enable to get new commits since last fetch * Enable maintenance of links from existing code files to Jira issues: add/delete links and add/delete code files based on changes in commits from git fetch * Update version to 2.2.8
1 parent c2fbb2d commit ab17559

File tree

9 files changed

+113
-65
lines changed

9 files changed

+113
-65
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<modelVersion>4.0.0</modelVersion>
77
<groupId>de.uhd.ifi.se.decision</groupId>
88
<artifactId>management.jira</artifactId>
9-
<version>2.2.7</version>
9+
<version>2.2.8</version>
1010
<organization>
1111
<name>Software Engineering Research Group, Heidelberg University</name>
1212
<url>https://github.com/cures-hub</url>

src/main/java/de/uhd/ifi/se/decision/management/jira/extraction/GitClient.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,8 @@
4040
*/
4141
public class GitClient {
4242

43-
public static final long REPO_OUTDATED_AFTER = 10 * 60 * 1000; // ex. 10 minutes = 10 minutes * 60 seconds * 1000
44-
// miliseconds
45-
private List<GitClientForSingleRepository> gitClientsForSingleRepos;
46-
4743
private String projectKey;
44+
private List<GitClientForSingleRepository> gitClientsForSingleRepos;
4845
private static final Logger LOGGER = LoggerFactory.getLogger(GitClient.class);
4946

5047
/**

src/main/java/de/uhd/ifi/se/decision/management/jira/extraction/versioncontrol/GitClientForSingleRepository.java

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.eclipse.jgit.lib.StoredConfig;
2424
import org.eclipse.jgit.revwalk.RevCommit;
2525
import org.eclipse.jgit.revwalk.RevWalk;
26-
import org.eclipse.jgit.transport.FetchResult;
2726
import org.eclipse.jgit.transport.RemoteConfig;
2827
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
2928
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
@@ -40,7 +39,15 @@
4039
import de.uhd.ifi.se.decision.management.jira.persistence.singlelocations.CodeClassPersistenceManager;
4140

4241
/**
43-
* Retrieves commits and code changes (diffs) from one git repository.
42+
* FIXME Investigate alternative for the following decision problem:
43+
*
44+
* @issue How can we assign more than one git repository to a Jira project?
45+
* @decision Implement class GitClientForSingleRepository with separate git
46+
* attribute for each repository!
47+
* @alternative Use git.remoteAdd() command to add new repos!
48+
*
49+
* Retrieves commits and code changes (diffs) from one git
50+
* repository.
4451
*/
4552
public class GitClientForSingleRepository {
4653

@@ -89,7 +96,7 @@ public boolean fetchOrClone() {
8996
if (gitDirectory.exists()) {
9097
if (openRepository(gitDirectory)) {
9198
if (!fetch()) {
92-
LOGGER.error("Failed Git pull " + workingDirectory);
99+
LOGGER.error("Failed Git fetch " + workingDirectory);
93100
return false;
94101
}
95102
} else {
@@ -121,35 +128,91 @@ private boolean openRepository(File directory) {
121128
return true;
122129
}
123130

131+
/**
132+
* @issue How can we get the new commits since the last fetch? To know the new
133+
* commits is important for trace link maintenance between Jira issues
134+
* and changed files (this includes updating/deleting changed files and
135+
* their links in database).
136+
* @decision Store the position of the default branch (e.g. master) before
137+
* fetching and after fetching to determine changes (in particular new
138+
* commits) since last fetch!
139+
* @alternative Use FetchResult and TrackingRefUpdate to determine changes (in
140+
* particular new commits) since last fetch!
141+
* @con Might work in productive code but did not work in unit testing.
142+
*
143+
* @return true if fetching was successful.
144+
*/
124145
public boolean fetch() {
125-
LOGGER.info("Pulling Repository: " + repoUri);
146+
LOGGER.info("Fetching Repository: " + repoUri);
126147
try {
127148
List<RemoteConfig> remotes = git.remoteList().call();
128149
for (RemoteConfig remote : remotes) {
129-
FetchResult fetchResult = git.fetch().setRemote(remote.getName()).setRefSpecs(remote.getFetchRefSpecs())
150+
ObjectId oldId = getDefaultBranchPosition();
151+
git.fetch().setRemote(remote.getName()).setRefSpecs(remote.getFetchRefSpecs())
130152
.setRemoveDeletedRefs(true).call();
153+
ObjectId newId = getDefaultBranchPosition();
154+
Diff diffSinceLastFetch = getDiffSinceLastFetch(oldId, newId);
155+
CodeClassPersistenceManager persistenceManager = new CodeClassPersistenceManager(projectKey);
156+
persistenceManager.maintainChangedFilesInDatabase(diffSinceLastFetch);
131157
LOGGER.info("Fetched branches in " + git.getRepository().getDirectory());
132-
// @issue How to maintain changed files extracted from git?
133-
// for (TrackingRefUpdate updateRes : fetchResult.getTrackingRefUpdates()) {
134-
// String refName = updateRes.getLocalName();
135-
// if (!refName.toLowerCase().contains(defaultBranchName.toLowerCase())) {
136-
// continue;
137-
// }
138-
// Diff diffSinceLastFetch = getDiff(updateRes.getOldObjectId(),
139-
// updateRes.getNewObjectId());
140-
// CodeClassPersistenceManager persistenceManager = new
141-
// CodeClassPersistenceManager(projectKey);
142-
// persistenceManager.maintainChangedFilesInDatabase(diffSinceLastFetch);
143-
// }
144158
}
145159
} catch (GitAPIException e) {
146-
LOGGER.error("Issue occurred while pulling from a remote." + "\n\t " + e.getMessage());
160+
LOGGER.error("Issue occurred while fetching from a remote." + "\n\t " + e.getMessage());
147161
return false;
148162
}
149-
LOGGER.info("Pulled from remote in " + git.getRepository().getDirectory());
163+
LOGGER.info("Fetched from remote in " + git.getRepository().getDirectory());
150164
return true;
151165
}
152166

167+
private ObjectId getDefaultBranchPosition() {
168+
ObjectId objectId = null;
169+
try {
170+
objectId = getRepository().resolve(getDefaultBranch().getName());
171+
} catch (RevisionSyntaxException | IOException | NullPointerException e) {
172+
}
173+
return objectId;
174+
}
175+
176+
public Diff getDiffSinceLastFetch(ObjectId oldObjectId, ObjectId newObjectId) {
177+
List<RevCommit> newCommits = getCommitsSinceLastFetch(oldObjectId, newObjectId);
178+
if (newCommits.isEmpty()) {
179+
return new Diff();
180+
}
181+
Diff diffSinceLastFetch = getDiff(newCommits.get(0), newCommits.get(newCommits.size() - 1));
182+
return addCommitsToChangedFiles(diffSinceLastFetch, newCommits);
183+
}
184+
185+
public Diff addCommitsToChangedFiles(Diff diff, List<RevCommit> commits) {
186+
for (RevCommit commit : commits) {
187+
List<DiffEntry> diffEntriesInCommit = getDiffEntries(commit);
188+
for (DiffEntry diffEntry : diffEntriesInCommit) {
189+
for (ChangedFile file : diff.getChangedFiles()) {
190+
if (diffEntry.getNewPath().contains(file.getName())) {
191+
file.addCommit(commit);
192+
}
193+
}
194+
}
195+
}
196+
return diff;
197+
}
198+
199+
/**
200+
* @return commits between the two git objects as a list of {@link RevCommit}s.
201+
*/
202+
private List<RevCommit> getCommitsSinceLastFetch(ObjectId oldObjectId, ObjectId newObjectId) {
203+
if (oldObjectId == null || newObjectId == null) {
204+
return new ArrayList<>();
205+
}
206+
List<RevCommit> newCommits = new ArrayList<>();
207+
try {
208+
Iterable<RevCommit> newCommitsIterable = git.log().addRange(oldObjectId, newObjectId).call();
209+
newCommitsIterable.iterator().forEachRemaining(newCommits::add);
210+
} catch (Exception e) {
211+
LOGGER.error("Issue occurred while fetching from a remote." + "\n\t " + e.getMessage());
212+
}
213+
return newCommits;
214+
}
215+
153216
private boolean cloneRepository(File directory) {
154217
if (repoUri == null || repoUri.isEmpty()) {
155218
return false;

src/main/java/de/uhd/ifi/se/decision/management/jira/persistence/singlelocations/CodeClassPersistenceManager.java

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public boolean deleteKnowledgeElement(long id, ApplicationUser user) {
5656
boolean isDeleted = false;
5757
for (CodeClassInDatabase databaseEntry : ACTIVE_OBJECTS.find(CodeClassInDatabase.class,
5858
Query.select().where("ID = ?", id))) {
59-
GenericLinkManager.deleteLinksForElement(id, DocumentationLocation.COMMIT);
59+
GenericLinkManager.deleteLinksForElement(id, documentationLocation);
6060
KnowledgeGraph.getOrCreate(projectKey).removeVertex(new ChangedFile(databaseEntry));
6161
isDeleted = CodeClassInDatabase.deleteElement(databaseEntry);
6262
}
@@ -104,7 +104,7 @@ public KnowledgeElement getKnowledgeElement(KnowledgeElement element) {
104104
public KnowledgeElement getKnowledgeElementByName(String name) {
105105
KnowledgeElement element = null;
106106
for (CodeClassInDatabase databaseEntry : ACTIVE_OBJECTS.find(CodeClassInDatabase.class,
107-
Query.select().where("FILE_NAME = ?", name))) {
107+
Query.select().where("PROJECT_KEY = ? AND FILE_NAME = ?", projectKey, name))) {
108108
element = new ChangedFile(databaseEntry);
109109
}
110110
return element;
@@ -120,15 +120,6 @@ public List<KnowledgeElement> getKnowledgeElements() {
120120
return knowledgeElements;
121121
}
122122

123-
public List<KnowledgeElement> getKnowledgeElementsMatchingName(String fileName) {
124-
List<KnowledgeElement> knowledgeElements = new ArrayList<>();
125-
for (CodeClassInDatabase databaseEntry : ACTIVE_OBJECTS.find(CodeClassInDatabase.class,
126-
Query.select().where("PROJECT_KEY = ? AND FILE_NAME = ?", projectKey, fileName))) {
127-
knowledgeElements.add(new ChangedFile(databaseEntry));
128-
}
129-
return knowledgeElements;
130-
}
131-
132123
@Override
133124
public List<Link> getInwardLinks(KnowledgeElement element) {
134125
return GenericLinkManager.getInwardLinks(element);
@@ -140,21 +131,22 @@ public List<Link> getOutwardLinks(KnowledgeElement element) {
140131
}
141132

142133
@Override
143-
public KnowledgeElement insertKnowledgeElement(KnowledgeElement changedFile, ApplicationUser user) {
144-
if (changedFile.getDocumentationLocation() != documentationLocation) {
134+
public KnowledgeElement insertKnowledgeElement(KnowledgeElement knowledgeElement, ApplicationUser user) {
135+
if (knowledgeElement.getDocumentationLocation() != documentationLocation) {
145136
return null;
146137
}
147-
ChangedFile existingElement = (ChangedFile) getKnowledgeElementByName(changedFile.getSummary());
138+
ChangedFile changedFile = (ChangedFile) knowledgeElement;
139+
ChangedFile existingElement = (ChangedFile) getKnowledgeElementByName(changedFile.getName());
148140
if (existingElement != null) {
149-
changedFile.setId(existingElement.getId());
150-
createLinksToJiraIssues((ChangedFile) changedFile, user);
141+
existingElement.setCommits(changedFile.getCommits());
142+
createLinksToJiraIssues(existingElement, user);
151143
return existingElement;
152144
}
153145
CodeClassInDatabase databaseEntry = ACTIVE_OBJECTS.create(CodeClassInDatabase.class);
154146
setParameters(changedFile, databaseEntry);
155147
databaseEntry.save();
156148
ChangedFile newElement = new ChangedFile(databaseEntry);
157-
newElement.setCommits(((ChangedFile) changedFile).getCommits());
149+
newElement.setCommits(changedFile.getCommits());
158150
createLinksToJiraIssues(newElement, user);
159151

160152
return newElement;
@@ -203,6 +195,9 @@ public CodeClassInDatabase findDatabaseEntry(KnowledgeElement element) {
203195
return entry;
204196
}
205197

198+
/**
199+
* @issue How to maintain changed files and links extracted from git?
200+
*/
206201
public void maintainChangedFilesInDatabase(Diff diff) {
207202
if (diff == null || diff.getChangedFiles().isEmpty()) {
208203
return;
@@ -213,35 +208,43 @@ public void maintainChangedFilesInDatabase(Diff diff) {
213208
}
214209
}
215210

216-
private void updateChangedFileInDatabase(ChangedFile changedFile) {
211+
public void updateChangedFileInDatabase(ChangedFile changedFile) {
217212
if (!changedFile.isJavaClass()) {
218213
return;
219214
}
220215
DiffEntry diffEntry = changedFile.getDiffEntry();
221216
switch (diffEntry.getChangeType()) {
222217
case ADD:
223-
insertKnowledgeElement(changedFile, null);
218+
// same as modify, thus, no break after add to fall through
224219
case MODIFY:
225-
// same as add, thus, no break after add to fall through
226220
// new links could have been added
221+
handleAdd(changedFile);
227222
break;
228223
case RENAME:
229224
handleRename(changedFile);
230225
break;
231226
case DELETE:
232-
deleteKnowledgeElement(changedFile, null);
227+
handleDelete(changedFile);
233228
break;
234229
default:
235230
break;
236231
}
237232
}
238233

239-
private void handleRename(ChangedFile changedFile) {
240-
KnowledgeElement oldFile = getKnowledgeElementByName(changedFile.getOldName());
241-
deleteKnowledgeElement(oldFile, null);
234+
private void handleAdd(ChangedFile changedFile) {
242235
insertKnowledgeElement(changedFile, null);
243236
}
244237

238+
private void handleDelete(ChangedFile changedFile) {
239+
KnowledgeElement fileToBeDeleted = getKnowledgeElementByName(changedFile.getOldName());
240+
deleteKnowledgeElement(fileToBeDeleted, null);
241+
}
242+
243+
private void handleRename(ChangedFile changedFile) {
244+
handleDelete(changedFile);
245+
handleAdd(changedFile);
246+
}
247+
245248
public void extractAllChangedFiles() {
246249
GitClient gitClient = GitClient.getOrCreate(projectKey);
247250
Diff diff = gitClient.getDiffOfEntireDefaultBranch();

src/main/java/de/uhd/ifi/se/decision/management/jira/rest/ViewRest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import de.uhd.ifi.se.decision.management.jira.model.KnowledgeType;
3838
import de.uhd.ifi.se.decision.management.jira.persistence.ConfigPersistenceManager;
3939
import de.uhd.ifi.se.decision.management.jira.persistence.KnowledgePersistenceManager;
40-
import de.uhd.ifi.se.decision.management.jira.persistence.singlelocations.CodeClassPersistenceManager;
4140
import de.uhd.ifi.se.decision.management.jira.view.decisionguidance.Recommendation;
4241
import de.uhd.ifi.se.decision.management.jira.view.decisionguidance.RecommendationEvaluation;
4342
import de.uhd.ifi.se.decision.management.jira.view.decisiontable.DecisionTable;
@@ -88,7 +87,6 @@ public Response getElementsOfFeatureBranchForJiraIssue(@Context HttpServletReque
8887
.entity(ImmutableMap.of("error", "Git extraction is disabled in project settings.")).build();
8988
}
9089
new CommitMessageToCommentTranscriber(issue).postCommitsIntoJiraIssueComments();
91-
new CodeClassPersistenceManager(projectKey).extractAllChangedFiles();
9290
return Response.ok(new DiffViewer(projectKey, issueKey)).build();
9391
}
9492

src/test/java/de/uhd/ifi/se/decision/management/jira/extraction/gitclient/TestGetBranches.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,10 @@
66

77
import org.eclipse.jgit.api.Git;
88
import org.eclipse.jgit.lib.Ref;
9-
import org.junit.Before;
109
import org.junit.Test;
1110

1211
public class TestGetBranches extends TestSetUpGit {
1312

14-
@Override
15-
@Before
16-
public void setUp() {
17-
super.setUp();
18-
}
19-
2013
@Test
2114
public void testGetAllBranches() {
2215
List<Ref> remoteBranches = gitClient.getBranches();

src/test/java/de/uhd/ifi/se/decision/management/jira/extraction/gitclient/TestSetUpGit.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ private static void makeExampleCommit(File inputFile, String targetName, String
226226
}
227227
}
228228

229-
private static void makeExampleCommit(String filename, String content, String commitMessage) {
229+
public static void makeExampleCommit(String filename, String content, String commitMessage) {
230230
Git git = gitClient.getGitClientsForSingleRepo(GIT_URI).getGit();
231231
try {
232232
File inputFile = new File(gitClient.getGitClientsForSingleRepo(GIT_URI).getGitDirectory().getParent(),

src/test/java/de/uhd/ifi/se/decision/management/jira/persistence/codeclasspersistencemanager/TestGetKnowledgeElement.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,4 @@ public void testGetEntryForKnowledgeElement() {
8080
public void testGetKnowledgeElements() {
8181
assertEquals(1, codeClassPersistenceManager.getKnowledgeElements().size());
8282
}
83-
84-
@Test
85-
@NonTransactional
86-
public void testGetKnowledgeElementsMatchingName() {
87-
assertEquals(1, codeClassPersistenceManager.getKnowledgeElementsMatchingName(classElement.getSummary()).size());
88-
}
8983
}

src/test/java/de/uhd/ifi/se/decision/management/jira/persistence/codeclasspersistencemanager/TestMaintainChangedFilesInDatabase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class TestMaintainChangedFilesInDatabase extends TestSetUpGit {
2020
@Override
2121
@Before
2222
public void setUp() {
23-
super.setUp();
23+
init();
2424
codeClassPersistenceManager = new CodeClassPersistenceManager("TEST");
2525
diff = gitClient.getDiffOfEntireDefaultBranch();
2626
}

0 commit comments

Comments
 (0)